From c3455e20b585b1e461bb309f0a6c11359403c7ce Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Wed, 4 Dec 2019 13:26:40 +0300
Subject: [PATCH 01/14] Add sharepoint synchronization service
---
...stant.ExternalStorages.Abstractions.csproj | 13 +
.../BaseCondition.cs | 20 ++
.../EqualCondition.cs | 13 +
.../ICondition.cs | 6 +
.../IExternalStorage.cs | 18 ++
.../PropertyNameParser.cs | 29 +++
.../StorageItem.cs | 23 ++
...t.ExternalStorages.SharepointOnline.csproj | 17 ++
.../Contracts/ISharepointAuthTokenService.cs | 11 +
.../ISharepointConditionsCompiler.cs | 11 +
.../Contracts/ISharepointFieldsMapper.cs | 12 +
.../ISharepointOnlineConfiguration.cs | 11 +
.../Contracts/ISharepointRequestExecutor.cs | 14 ++
.../Contracts/SharepointRequest.cs | 120 ++++++++++
.../SharepointApiModels/SharepointListItem.cs | 33 +++
.../SharepointListItemRequest.cs | 42 ++++
.../SharepointListItemsResponse.cs | 9 +
.../SharepointListResponse.cs | 7 +
.../SharepointAuthTokenService.cs | 210 +++++++++++++++++
.../SharepointConditionsCompiler.cs | 58 +++++
.../SharepointFieldsMapper.cs | 78 ++++++
.../SharepointOnlineConfiguration.cs | 13 +
.../SharepointRequestExecutor.cs | 62 +++++
.../SharepointStorage.cs | 223 ++++++++++++++++++
.../Arcadia.Assistant.Sharepoint.csproj | 16 ++
.../PackageRoot/Config/Settings.xml | 11 +
.../PackageRoot/ServiceManifest.xml | 35 +++
.../Arcadia.Assistant.Sharepoint/Program.cs | 38 +++
.../ServiceEventSource.cs | 175 ++++++++++++++
.../Sharepoint.cs | 52 ++++
.../Arcadia.Assistant/Arcadia.Assistant.sln | 152 ++++++++++++
31 files changed, 1532 insertions(+)
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/BaseCondition.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/EqualCondition.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/ICondition.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/IExternalStorage.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointAuthTokenService.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointConditionsCompiler.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointOnlineConfiguration.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointRequestExecutor.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListResponse.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointConditionsCompiler.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/ServiceManifest.xml
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj
new file mode 100644
index 000000000..3059692d3
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.1
+ AnyCPU;x64
+ enable
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/BaseCondition.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/BaseCondition.cs
new file mode 100644
index 000000000..60aecc71f
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/BaseCondition.cs
@@ -0,0 +1,20 @@
+namespace Arcadia.Assistant.ExternalStorages.Abstractions
+{
+ using System;
+ using System.Linq.Expressions;
+
+ public abstract class BaseCondition : ICondition
+ {
+ protected BaseCondition(Expression> property, object value)
+ {
+ new PropertyNameParser().EnsureExpressionIsProperty(property);
+
+ this.Property = property;
+ this.Value = value;
+ }
+
+ public Expression> Property { get; }
+
+ public object Value { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/EqualCondition.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/EqualCondition.cs
new file mode 100644
index 000000000..64fcc9100
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/EqualCondition.cs
@@ -0,0 +1,13 @@
+namespace Arcadia.Assistant.ExternalStorages.Abstractions
+{
+ using System;
+ using System.Linq.Expressions;
+
+ public class EqualCondition : BaseCondition
+ {
+ public EqualCondition(Expression> property, object value)
+ : base(property, value)
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/ICondition.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/ICondition.cs
new file mode 100644
index 000000000..630b4e76a
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/ICondition.cs
@@ -0,0 +1,6 @@
+namespace Arcadia.Assistant.ExternalStorages.Abstractions
+{
+ public interface ICondition
+ {
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/IExternalStorage.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/IExternalStorage.cs
new file mode 100644
index 000000000..92256a8ca
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/IExternalStorage.cs
@@ -0,0 +1,18 @@
+namespace Arcadia.Assistant.ExternalStorages.Abstractions
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ public interface IExternalStorage : IDisposable
+ {
+ Task> GetItems(string list, IEnumerable? conditions = null, CancellationToken cancellationToken = default);
+
+ Task AddItem(string list, StorageItem item, CancellationToken cancellationToken = default);
+
+ Task UpdateItem(string list, StorageItem item, CancellationToken cancellationToken = default);
+
+ Task DeleteItem(string list, string itemId, CancellationToken cancellationToken = default);
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs
new file mode 100644
index 000000000..a76a6389e
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs
@@ -0,0 +1,29 @@
+namespace Arcadia.Assistant.ExternalStorages.Abstractions
+{
+ using System;
+ using System.Linq.Expressions;
+ using System.Reflection;
+
+ public class PropertyNameParser
+ {
+ public void EnsureExpressionIsProperty(Expression> expression)
+ {
+ this.GetName(expression);
+ }
+
+ public string GetName(Expression> expression)
+ {
+ switch (expression.Body)
+ {
+ case MemberExpression member when member.Member.MemberType == MemberTypes.Property:
+ return member.Member.Name;
+
+ case UnaryExpression unary when unary.Operand is MemberExpression operand && operand.Member.MemberType == MemberTypes.Property:
+ return operand.Member.Name;
+
+ default:
+ throw new ArgumentException("Expression is not a property", nameof(expression));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs
new file mode 100644
index 000000000..30dace379
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs
@@ -0,0 +1,23 @@
+namespace Arcadia.Assistant.ExternalStorages.Abstractions
+{
+ using System;
+
+ public class StorageItem
+ {
+ public string Id { get; set; } = string.Empty;
+
+ public string Title { get; set; } = string.Empty;
+
+ public string Description { get; set; } = string.Empty;
+
+ public DateTime StartDate { get; set; }
+
+ public DateTime EndDate { get; set; }
+
+ public string Category { get; set; } = string.Empty;
+
+ public bool AllDayEvent { get; set; }
+
+ public string CalendarEventId { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj
new file mode 100644
index 000000000..63ca4a47c
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netstandard2.1
+ AnyCPU;x64
+ enable
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointAuthTokenService.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointAuthTokenService.cs
new file mode 100644
index 000000000..ccccd3721
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointAuthTokenService.cs
@@ -0,0 +1,11 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.Contracts
+{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ public interface ISharepointAuthTokenService : IDisposable
+ {
+ Task GetAccessToken(string sharepointUrl, CancellationToken cancellationToken = default);
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointConditionsCompiler.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointConditionsCompiler.cs
new file mode 100644
index 000000000..6087be6e3
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointConditionsCompiler.cs
@@ -0,0 +1,11 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.Contracts
+{
+ using System.Collections.Generic;
+
+ using Abstractions;
+
+ public interface ISharepointConditionsCompiler
+ {
+ string? CompileConditions(IEnumerable? conditions = null);
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
new file mode 100644
index 000000000..f27d2ffb8
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
@@ -0,0 +1,12 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.Contracts
+{
+ using System;
+ using System.Linq.Expressions;
+
+ using Abstractions;
+
+ public interface ISharepointFieldsMapper
+ {
+ string GetSharepointField(Expression> property);
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointOnlineConfiguration.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointOnlineConfiguration.cs
new file mode 100644
index 000000000..6a3d028c1
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointOnlineConfiguration.cs
@@ -0,0 +1,11 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.Contracts
+{
+ public interface ISharepointOnlineConfiguration
+ {
+ string ServerUrl { get; }
+
+ string ClientId { get; }
+
+ string ClientSecret { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointRequestExecutor.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointRequestExecutor.cs
new file mode 100644
index 000000000..c5be0a253
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointRequestExecutor.cs
@@ -0,0 +1,14 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.Contracts
+{
+ using System;
+ using System.Net.Http;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ public interface ISharepointRequestExecutor : IDisposable
+ {
+ Task ExecuteSharepointRequest(SharepointRequest request, CancellationToken cancellationToken = default);
+
+ Task ExecuteSharepointRequest(SharepointRequest request, CancellationToken cancellationToken = default);
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
new file mode 100644
index 000000000..abbae1b1e
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
@@ -0,0 +1,120 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.Contracts
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+
+ using Newtonsoft.Json;
+
+ public class SharepointRequest
+ {
+ private const string AcceptHeaderName = "Accept";
+ private const string AuthorizationHeaderName = "Authorization";
+ private const string ContentTypeHeaderName = "Content-Type";
+ private const string ContentLengthHeaderName = "Content-Length";
+ private const string IfMatchHeaderName = "IF-MATCH";
+ private const string XHttpMethodHeaderName = "X-HTTP-Method";
+
+ private readonly List> headersInternal = new List>();
+
+ private SharepointRequest(HttpMethod httpMethod, string url)
+ {
+ this.HttpMethod = httpMethod;
+ this.Url = url;
+ }
+
+ public HttpMethod HttpMethod { get; }
+
+ public string Url { get; }
+
+ public HttpContent? Content { get; private set; }
+
+ public IReadOnlyList> Headers => this.headersInternal;
+
+ public static SharepointRequest Create(HttpMethod httpMethod, string url)
+ {
+ return new SharepointRequest(httpMethod, url);
+ }
+
+ public SharepointRequest WithAcceptHeader(string value)
+ {
+ this.AddHeader(AcceptHeaderName, value);
+ return this;
+ }
+
+ public SharepointRequest WithBearerAuthorizationHeader(string value)
+ {
+ this.AddHeader(AuthorizationHeaderName, value);
+ return this;
+ }
+
+ public SharepointRequest WithContent(object content)
+ {
+ var contentString = JsonConvert.SerializeObject(content);
+ this.Content = new StringContent(contentString);
+
+ this.AddHeader(ContentTypeHeaderName, "application/json;odata=verbose");
+ this.AddHeader(ContentLengthHeaderName, contentString.Length.ToString());
+
+ return this;
+ }
+
+ public SharepointRequest WithIfMatchHeader()
+ {
+ this.AddHeader(IfMatchHeaderName, "*");
+ return this;
+ }
+
+ public SharepointRequest WithXHttpMethodHeader(string value)
+ {
+ this.AddHeader(XHttpMethodHeaderName, value);
+ return this;
+ }
+
+ public HttpRequestMessage GetHttpRequest()
+ {
+ var httpRequest = new HttpRequestMessage(this.HttpMethod, this.Url);
+
+ if (this.Content != null)
+ {
+ httpRequest.Content = this.Content;
+ }
+
+ foreach (var (headerName, headerValue) in this.headersInternal)
+ {
+ switch (headerName)
+ {
+ case AcceptHeaderName:
+ httpRequest.Headers.Accept.Clear();
+ httpRequest.Headers.Accept.TryParseAdd(headerValue);
+ break;
+
+ case AuthorizationHeaderName:
+ httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", headerValue);
+ break;
+
+ case ContentTypeHeaderName:
+ httpRequest.Content.Headers.Remove(headerName);
+ httpRequest.Content.Headers.TryAddWithoutValidation(headerName, headerValue);
+ break;
+
+ case ContentLengthHeaderName:
+ httpRequest.Content.Headers.ContentLength = long.Parse(headerValue);
+ break;
+
+ default:
+ httpRequest.Headers.TryAddWithoutValidation(headerName, headerValue);
+ break;
+ }
+ }
+
+ return httpRequest;
+ }
+
+ private void AddHeader(string name, string value)
+ {
+ this.headersInternal.Add(Tuple.Create(name, value));
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs
new file mode 100644
index 000000000..dc82209f6
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs
@@ -0,0 +1,33 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.SharepointApiModels
+{
+ using System;
+ using System.Runtime.Serialization;
+
+ [DataContract]
+ public class SharepointListItem
+ {
+ [DataMember]
+ public int Id { get; set; }
+
+ [DataMember]
+ public string Title { get; set; } = string.Empty;
+
+ [DataMember]
+ public string Description { get; set; } = string.Empty;
+
+ [DataMember]
+ public DateTime EventDate { get; set; }
+
+ [DataMember]
+ public DateTime EndDate { get; set; }
+
+ [DataMember]
+ public string Category { get; set; } = string.Empty;
+
+ [DataMember(Name = "fAllDayEvent")]
+ public bool AllDayEvent { get; set; }
+
+ [DataMember]
+ public string CalendarEventId { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
new file mode 100644
index 000000000..07f956911
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
@@ -0,0 +1,42 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.SharepointApiModels
+{
+ using System.Runtime.Serialization;
+
+ [DataContract]
+ public class SharepointListItemRequest
+ {
+ [DataMember(Name = "__metadata")]
+ public MetadataRequest Metadata { get; set; } = new MetadataRequest();
+
+ [DataMember]
+ public int Id { get; set; }
+
+ [DataMember]
+ public string Title { get; set; } = string.Empty;
+
+ [DataMember]
+ public string Description { get; set; } = string.Empty;
+
+ [DataMember]
+ public string EventDate { get; set; } = string.Empty;
+
+ [DataMember]
+ public string EndDate { get; set; } = string.Empty;
+
+ [DataMember]
+ public string Category { get; set; } = string.Empty;
+
+ [DataMember(Name = "fAllDayEvent")]
+ public bool AllDayEvent { get; set; }
+
+ [DataMember]
+ public string CalendarEventId { get; set; } = string.Empty;
+
+ [DataContract]
+ public class MetadataRequest
+ {
+ [DataMember(Name = "type")]
+ public string? Type { get; set; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs
new file mode 100644
index 000000000..2ea6c9070
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs
@@ -0,0 +1,9 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.SharepointApiModels
+{
+ using System.Collections.Generic;
+
+ public class SharepointListItemsResponse
+ {
+ public IEnumerable? Value { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListResponse.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListResponse.cs
new file mode 100644
index 000000000..568e92e5b
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListResponse.cs
@@ -0,0 +1,7 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.SharepointApiModels
+{
+ public class SharepointListResponse
+ {
+ public string? ListItemEntityTypeFullName { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
new file mode 100644
index 000000000..7beea5d0a
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
@@ -0,0 +1,210 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Globalization;
+ using System.Linq;
+ using System.Net;
+ using System.Net.Http;
+ using System.Net.Http.Headers;
+ using System.Runtime.Serialization;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using System.Web;
+
+ using Contracts;
+
+ using Newtonsoft.Json;
+
+ public class SharepointAuthTokenService : ISharepointAuthTokenService
+ {
+ private const string SharePointPrincipal = "00000003-0000-0ff1-ce00-000000000000";
+
+ private const string AcsMetadataEndpoint = "https://accounts.accesscontrol.windows.net/metadata/json/1";
+ private const string OAuth2GrantType = "client_credentials";
+ private const string OAuth2Protocol = "OAuth2";
+
+ private readonly ISharepointOnlineConfiguration configuration;
+ private readonly HttpClient httpClient;
+
+ public SharepointAuthTokenService(ISharepointOnlineConfiguration configuration, IHttpClientFactory httpClientFactory)
+ {
+ this.configuration = configuration;
+ this.httpClient = httpClientFactory.CreateClient();
+ }
+
+ public async Task GetAccessToken(string sharepointUrl, CancellationToken cancellationToken)
+ {
+ var realm = await this.GetRealm(sharepointUrl, cancellationToken);
+ var hostName = new Uri(sharepointUrl).Authority;
+
+ var oauthClientId = this.GetFormattedPrincipal(this.configuration.ClientId, null, realm);
+ var oauthResource = this.GetFormattedPrincipal(SharePointPrincipal, hostName, realm);
+
+ var accessToken = await this.IssueToken(
+ realm,
+ oauthClientId,
+ this.configuration.ClientSecret,
+ oauthResource,
+ cancellationToken);
+
+ if (accessToken == null)
+ {
+ throw new Exception("Access token is null");
+ }
+
+ return accessToken;
+ }
+
+ public void Dispose()
+ {
+ this.httpClient.Dispose();
+ }
+
+ private async Task GetRealm(string urlSharePoint, CancellationToken cancellationToken)
+ {
+ var request = new HttpRequestMessage(HttpMethod.Get, urlSharePoint + "/_vti_bin/client.svc");
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", string.Empty);
+
+ var response = await this.httpClient.SendAsync(request, cancellationToken);
+
+ if (response.StatusCode != HttpStatusCode.Unauthorized || response.Headers.WwwAuthenticate == null)
+ {
+ throw new HttpRequestException("Not supported response to a realm request");
+ }
+
+ var wwwAuthenticateHeaderValue = response.Headers.WwwAuthenticate.ToString();
+
+ var realm = this.GetRealmFromHeader(wwwAuthenticateHeaderValue);
+
+ if (realm == null)
+ {
+ throw new HttpRequestException("Not supported response to a realm request");
+ }
+
+ return realm;
+ }
+
+ private string? GetRealmFromHeader(string headerValue)
+ {
+ const string bearer = "Bearer realm=\"";
+ const int realmLength = 36;
+
+ var bearerIndex = headerValue.IndexOf(bearer, StringComparison.Ordinal);
+
+ if (bearerIndex < 0)
+ {
+ return null;
+ }
+
+ var realmStartIndex = bearerIndex + bearer.Length;
+
+ if (headerValue.Length < realmStartIndex + realmLength)
+ {
+ return null;
+ }
+
+ var realm = headerValue.Substring(realmStartIndex, realmLength);
+
+ return !Guid.TryParse(realm, out _) ? null : realm;
+ }
+
+ private async Task IssueToken(
+ string realm,
+ string tokenClientId,
+ string tokenClientSecret,
+ string tokenResource,
+ CancellationToken cancellationToken)
+ {
+ var tokenUrl = await this.GetOAuth2Url(realm, cancellationToken);
+
+ var requestData =
+ "grant_type=" + HttpUtility.UrlEncode(OAuth2GrantType) +
+ "&client_id=" + HttpUtility.UrlEncode(tokenClientId) +
+ "&client_secret=" + HttpUtility.UrlEncode(tokenClientSecret) +
+ "&resource=" + HttpUtility.UrlEncode(tokenResource);
+
+ var request = new HttpRequestMessage(HttpMethod.Post, tokenUrl)
+ {
+ Content = new StringContent(requestData)
+ };
+
+ request.Content.Headers.Remove("Content-Type");
+ request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
+ request.Content.Headers.ContentLength = requestData.Length;
+
+ var response = await this.httpClient.SendAsync(request, cancellationToken);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new HttpRequestException("Access token response has failed");
+ }
+
+ var responseContent = await response.Content.ReadAsStringAsync();
+
+ var oauthResult = JsonConvert.DeserializeObject(responseContent);
+ return oauthResult.AccessToken;
+ }
+
+ private async Task GetOAuth2Url(string realm, CancellationToken cancellationToken)
+ {
+ var metadata = await this.GetMetadataDocument(realm, cancellationToken);
+
+ var s2SEndpoint = metadata.Endpoints?.SingleOrDefault(e => e.Protocol == OAuth2Protocol);
+
+ if (s2SEndpoint != null)
+ {
+ return s2SEndpoint.Location;
+ }
+
+ throw new Exception("Metadata document does not contain OAuth2 endpoint url");
+ }
+
+ private async Task GetMetadataDocument(string realm, CancellationToken cancellationToken)
+ {
+ var metadataEndpointUrl = $"{AcsMetadataEndpoint}?realm={realm}";
+
+ var response = await this.httpClient.GetAsync(metadataEndpointUrl, cancellationToken);
+ var metadataString = await response.Content.ReadAsStringAsync();
+ var metadata = JsonConvert.DeserializeObject(metadataString);
+
+ if (metadata == null)
+ {
+ throw new Exception($"No metadata document found at the global endpoint {metadataEndpointUrl}");
+ }
+
+ return metadata;
+ }
+
+ private string GetFormattedPrincipal(string principalName, string? hostName, string realm)
+ {
+ return string.IsNullOrEmpty(hostName)
+ ? string.Format(CultureInfo.InvariantCulture, "{0}@{1}", principalName, realm)
+ : string.Format(CultureInfo.InvariantCulture, "{0}/{1}@{2}", principalName, hostName, realm);
+ }
+
+ [DataContract]
+ private class JsonMetadataDocument
+ {
+ [DataMember(Name = "endpoints")]
+ public IEnumerable? Endpoints { get; set; }
+ }
+
+ [DataContract]
+ private class JsonEndpoint
+ {
+ [DataMember(Name = "location")]
+ public string? Location { get; set; }
+
+ [DataMember(Name = "protocol")]
+ public string? Protocol { get; set; }
+ }
+
+ [DataContract]
+ private class OAuth2AccessTokenResponse
+ {
+ [DataMember(Name = "access_token")]
+ public string? AccessToken { get; set; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointConditionsCompiler.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointConditionsCompiler.cs
new file mode 100644
index 000000000..8af12f335
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointConditionsCompiler.cs
@@ -0,0 +1,58 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Web;
+
+ using Abstractions;
+
+ using Contracts;
+
+ public class SharepointConditionsCompiler : ISharepointConditionsCompiler
+ {
+ private readonly ISharepointFieldsMapper fieldsMapper;
+
+ public SharepointConditionsCompiler(ISharepointFieldsMapper fieldsMapper)
+ {
+ this.fieldsMapper = fieldsMapper;
+ }
+
+ public string? CompileConditions(IEnumerable? conditions = null)
+ {
+ if (conditions == null)
+ {
+ return null;
+ }
+
+ var compiledConditions = conditions
+ .Select(this.CompileCondition);
+
+ return $"$filter={HttpUtility.UrlEncode(string.Join(" and ", compiledConditions))}";
+ }
+
+ private string CompileCondition(ICondition condition)
+ {
+ return condition switch
+ {
+ EqualCondition equalCondition => this.GetEqualCompileCondition(equalCondition),
+
+ _ => throw new ArgumentException($"Not supported condition type: {condition.GetType()}", nameof(condition)),
+ };
+ }
+
+ private string GetEqualCompileCondition(BaseCondition equalCondition)
+ {
+ var sharepointField = this.fieldsMapper.GetSharepointField(equalCondition.Property);
+
+ if (equalCondition.Value is DateTime dateTimeValue)
+ {
+ throw new NotImplementedException("Filter by datetime is not supported yet.");
+ }
+
+ var fieldValue = equalCondition.Value;
+
+ return $"{sharepointField} eq '{fieldValue}'";
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
new file mode 100644
index 000000000..a9241e03f
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
@@ -0,0 +1,78 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Linq.Expressions;
+
+ using Abstractions;
+
+ using Contracts;
+
+ public class SharepointFieldsMapper : ISharepointFieldsMapper
+ {
+ private static readonly SharepointFieldMapping CalendarEventIdMapping =
+ CreateMapping(item => item.CalendarEventId, "CalendarEventId");
+
+ private readonly Dictionary fieldsByPropertyName;
+
+ public SharepointFieldsMapper()
+ : this(DefaultMappingWithCalendarEventId)
+ {
+ }
+
+ public SharepointFieldsMapper(params SharepointFieldMapping[] fields)
+ {
+ this.fieldsByPropertyName = fields
+ .ToDictionary(f => this.GetPropertyName(f.Property), f => f.Field);
+ }
+
+ public static IEnumerable DefaultMapping { get; } = new[]
+ {
+ CreateMapping(item => item.Id, "ID"),
+ CreateMapping(item => item.Title, "Title"),
+ CreateMapping(item => item.Description, "Description"),
+ CreateMapping(item => item.StartDate, "EventDate"),
+ CreateMapping(item => item.EndDate, "EndDate"),
+ CreateMapping(item => item.Category, "Category"),
+ CreateMapping(item => item.AllDayEvent, "fAllDayEvent")
+ };
+
+ private static SharepointFieldMapping[] DefaultMappingWithCalendarEventId { get; } = DefaultMapping.Union(new[] { CalendarEventIdMapping }).ToArray();
+
+ public string GetSharepointField(Expression> property)
+ {
+ var propertyName = this.GetPropertyName(property);
+
+ if (this.fieldsByPropertyName.TryGetValue(propertyName, out var field))
+ {
+ return field;
+ }
+
+ throw new ArgumentException($"Mapping for property '{propertyName}' doesn't exist");
+ }
+
+ public static SharepointFieldMapping CreateMapping(Expression> property, string fieldName)
+ {
+ return new SharepointFieldMapping(property, fieldName);
+ }
+
+ private string GetPropertyName(Expression> property)
+ {
+ return new PropertyNameParser().GetName(property);
+ }
+
+ public struct SharepointFieldMapping
+ {
+ public SharepointFieldMapping(Expression> property, string field)
+ {
+ this.Property = property;
+ this.Field = field;
+ }
+
+ public Expression> Property { get; }
+
+ public string Field { get; }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
new file mode 100644
index 000000000..6558169b7
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
@@ -0,0 +1,13 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
+{
+ using Contracts;
+
+ public class SharepointOnlineConfiguration : ISharepointOnlineConfiguration
+ {
+ public string ServerUrl { get; set; } = string.Empty;
+
+ public string ClientId { get; set; } = string.Empty;
+
+ public string ClientSecret { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs
new file mode 100644
index 000000000..b62f1e703
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs
@@ -0,0 +1,62 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
+{
+ using System.Net.Http;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ using Contracts;
+
+ using Newtonsoft.Json;
+
+ public class SharepointRequestExecutor : ISharepointRequestExecutor
+ {
+ private readonly ISharepointAuthTokenService authTokenService;
+ private readonly ISharepointOnlineConfiguration configuration;
+ private readonly HttpClient httpClient;
+
+ private string? accessToken;
+
+ public SharepointRequestExecutor(
+ ISharepointOnlineConfiguration configuration,
+ ISharepointAuthTokenService authTokenService,
+ IHttpClientFactory httpClientFactory)
+ {
+ this.configuration = configuration;
+ this.authTokenService = authTokenService;
+ this.httpClient = httpClientFactory.CreateClient();
+ }
+
+ public async Task ExecuteSharepointRequest(SharepointRequest request, CancellationToken cancellationToken = default)
+ {
+ var response = await this.ExecuteSharepointRequest(request, cancellationToken);
+
+ if (!response.IsSuccessStatusCode)
+ {
+ throw new HttpRequestException($"Request failed with {response.StatusCode} status code");
+ }
+
+ var content = await response.Content.ReadAsStringAsync();
+ return JsonConvert.DeserializeObject(content);
+ }
+
+ public async Task ExecuteSharepointRequest(SharepointRequest request, CancellationToken cancellationToken = default)
+ {
+ // To cache access token for several Sharepoint requests in bounds of one request to storage
+ if (this.accessToken == null)
+ {
+ this.accessToken = await this.authTokenService.GetAccessToken(this.configuration.ServerUrl, cancellationToken);
+ }
+
+ request = request
+ .WithAcceptHeader("application/json;odata=nometadata")
+ .WithBearerAuthorizationHeader(this.accessToken);
+
+ return await this.httpClient.SendAsync(request.GetHttpRequest(), cancellationToken);
+ }
+
+ public void Dispose()
+ {
+ this.httpClient.Dispose();
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
new file mode 100644
index 000000000..bf8c7d502
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
@@ -0,0 +1,223 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Net.Http;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ using Abstractions;
+
+ using Contracts;
+
+ using SharepointApiModels;
+
+ public class SharepointStorage : IExternalStorage
+ {
+ private readonly ISharepointConditionsCompiler conditionsCompiler;
+ private readonly ISharepointOnlineConfiguration configuration;
+ private readonly ISharepointFieldsMapper fieldsMapper;
+ private readonly ISharepointRequestExecutor requestExecutor;
+
+ public SharepointStorage(
+ ISharepointOnlineConfiguration configuration,
+ ISharepointRequestExecutor requestExecutor,
+ ISharepointFieldsMapper sharePointFieldsMapper,
+ ISharepointConditionsCompiler sharePointConditionsCompiler)
+ {
+ this.configuration = configuration;
+ this.requestExecutor = requestExecutor;
+ this.fieldsMapper = sharePointFieldsMapper;
+ this.conditionsCompiler = sharePointConditionsCompiler;
+ }
+
+ public async Task> GetItems(string list, IEnumerable? conditions, CancellationToken cancellationToken)
+ {
+ var listItems = await this.GetListItems(list, conditions, cancellationToken);
+
+ return listItems
+ .Select(this.ListItemToStorageItem)
+ .ToArray();
+ }
+
+ public async Task AddItem(string list, StorageItem item, CancellationToken cancellationToken)
+ {
+ var listItemType = await this.GetListItemType(list, cancellationToken);
+ var requestData = this.StorageItemToListItemRequest(item, listItemType);
+
+ var request = SharepointRequest
+ .Create(HttpMethod.Post, this.GetListItemsUrl(list))
+ .WithContent(requestData);
+
+ var addedItem = await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
+
+ return this.ListItemToStorageItem(addedItem);
+ }
+
+ public async Task UpdateItem(string list, StorageItem item, CancellationToken cancellationToken)
+ {
+ var idConditions = new[] { new EqualCondition(x => x.Id, item.Id) };
+ var listItems = (await this.GetListItems(list, idConditions, cancellationToken)).ToArray();
+
+ this.EnsureSingleItemReturned(listItems);
+
+ var existingListItem = listItems.First();
+
+ var updateItemUrl = this.GetListItemsUrl(list, false);
+ updateItemUrl += $"({existingListItem.Id})";
+
+ var listItemType = await this.GetListItemType(list, cancellationToken);
+
+ var requestData = this.StorageItemToListItemRequest(item, listItemType);
+
+ var request = SharepointRequest
+ .Create(HttpMethod.Post, updateItemUrl)
+ .WithContent(requestData)
+ .WithIfMatchHeader()
+ .WithXHttpMethodHeader("MERGE");
+
+ await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
+ }
+
+ public async Task DeleteItem(string list, string itemId, CancellationToken cancellationToken)
+ {
+ var idConditions = new[] { new EqualCondition(x => x.Id, itemId) };
+ var listItems = (await this.GetListItems(list, idConditions, cancellationToken)).ToArray();
+
+ this.EnsureSingleItemReturned(listItems);
+
+ var existingListItem = listItems.First();
+
+ var deleteItemUrl = this.GetListItemsUrl(list, false);
+ deleteItemUrl += $"({existingListItem.Id})";
+
+ var request = SharepointRequest
+ .Create(HttpMethod.Post, deleteItemUrl)
+ .WithIfMatchHeader()
+ .WithXHttpMethodHeader("DELETE");
+
+ await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
+ }
+
+ public void Dispose()
+ {
+ }
+
+ private async Task?> GetListItems(
+ string list,
+ IEnumerable? conditions,
+ CancellationToken cancellationToken)
+ {
+ var itemsUrl = this.GetListItemsUrl(list);
+
+ var conditionsUrlPart = this.conditionsCompiler.CompileConditions(conditions);
+
+ if (conditionsUrlPart != null)
+ {
+ itemsUrl += $"&{conditionsUrlPart}";
+ }
+
+ var request = SharepointRequest.Create(HttpMethod.Get, itemsUrl);
+ var response = await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
+
+ return response.Value;
+ }
+
+ private async Task GetListItemType(string list, CancellationToken cancellationToken)
+ {
+ var listUrl = $"{this.GetListUrl(list)}?$select=ListItemEntityTypeFullName";
+ var request = SharepointRequest.Create(HttpMethod.Get, listUrl);
+ var response = await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
+ return response.ListItemEntityTypeFullName;
+ }
+
+ private string GetListUrl(string list)
+ {
+ return $"{this.configuration.ServerUrl}/_api/lists/GetByTitle('{list}')";
+ }
+
+ private string GetListItemsUrl(string list, bool includeSelectPart = true)
+ {
+ var baseUrl = $"{this.GetListUrl(list)}/items";
+
+ var fieldNames = this.GetFieldNames();
+
+ var selectUrlPart =
+ $"$select={fieldNames.Id},{fieldNames.Title},{fieldNames.Description},{fieldNames.StartDate}," +
+ $"{fieldNames.EndDate},{fieldNames.Category},{fieldNames.AllDayEvent},{fieldNames.CalendarEventId}";
+
+ return !includeSelectPart
+ ? baseUrl
+ : $"{baseUrl}?{selectUrlPart}";
+ }
+
+ private StorageItem ListItemToStorageItem(SharepointListItem item)
+ {
+ return new StorageItem
+ {
+ Id = item.Id.ToString(),
+ Title = item.Title,
+ Description = item.Description,
+ StartDate = item.EventDate,
+ EndDate = item.EndDate,
+ Category = item.Category,
+ AllDayEvent = item.AllDayEvent,
+ CalendarEventId = item.CalendarEventId
+ };
+ }
+
+ private SharepointListItemRequest StorageItemToListItemRequest(StorageItem item, string? listItemType)
+ {
+ return new SharepointListItemRequest
+ {
+ Metadata = new SharepointListItemRequest.MetadataRequest
+ {
+ Type = listItemType
+ },
+ Title = item.Title,
+ Description = item.Description,
+ EventDate = item.StartDate.ToString("d"),
+ EndDate = item.EndDate.ToString("d"),
+ Category = item.Category,
+ AllDayEvent = item.AllDayEvent,
+ CalendarEventId = item.CalendarEventId
+ };
+ }
+
+ private void EnsureSingleItemReturned(SharepointListItem[] listItems)
+ {
+ if (listItems.Length == 0)
+ {
+ throw new ArgumentException("No items were found by specified conditions");
+ }
+
+ if (listItems.Length > 1)
+ {
+ throw new ArgumentException("More than one item was found by specified conditions");
+ }
+ }
+
+ private (
+ string Id,
+ string Title,
+ string Description,
+ string StartDate,
+ string EndDate,
+ string Category,
+ string AllDayEvent,
+ string CalendarEventId
+ ) GetFieldNames()
+ {
+ var idField = this.fieldsMapper.GetSharepointField(si => si.Id);
+ var titleField = this.fieldsMapper.GetSharepointField(si => si.Title);
+ var descriptionField = this.fieldsMapper.GetSharepointField(si => si.Description);
+ var startDateField = this.fieldsMapper.GetSharepointField(si => si.StartDate);
+ var endDateField = this.fieldsMapper.GetSharepointField(si => si.EndDate);
+ var categoryField = this.fieldsMapper.GetSharepointField(si => si.Category);
+ var allDayEvent = this.fieldsMapper.GetSharepointField(si => si.AllDayEvent);
+ var calendarEventIdField = this.fieldsMapper.GetSharepointField(si => si.CalendarEventId);
+ return (idField, titleField, descriptionField, startDateField, endDateField, categoryField, allDayEvent, calendarEventIdField);
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
new file mode 100644
index 000000000..1725162b7
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ netcoreapp3.0
+ True
+ True
+ win7-x64
+ False
+
+
+
+
+
+
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
new file mode 100644
index 000000000..aea0ce165
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/ServiceManifest.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/ServiceManifest.xml
new file mode 100644
index 000000000..76632f8c7
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/ServiceManifest.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Arcadia.Assistant.Sharepoint.exe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
new file mode 100644
index 000000000..e0ce85202
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
@@ -0,0 +1,38 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System;
+ using System.Diagnostics;
+ using System.Threading;
+
+ using Microsoft.ServiceFabric.Services.Runtime;
+
+ internal static class Program
+ {
+ ///
+ /// This is the entry point of the service host process.
+ ///
+ private static void Main()
+ {
+ try
+ {
+ // The ServiceManifest.XML file defines one or more service type names.
+ // Registering a service maps a service type name to a .NET type.
+ // When Service Fabric creates an instance of this service type,
+ // an instance of the class is created in this host process.
+
+ ServiceRuntime.RegisterServiceAsync("Arcadia.Assistant.SharepointType",
+ context => new Sharepoint(context)).GetAwaiter().GetResult();
+
+ ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Sharepoint).Name);
+
+ // Prevents this host process from terminating so services keep running.
+ Thread.Sleep(Timeout.Infinite);
+ }
+ catch (Exception e)
+ {
+ ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
+ throw;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs
new file mode 100644
index 000000000..642073ea7
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs
@@ -0,0 +1,175 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System;
+ using System.Diagnostics.Tracing;
+ using System.Fabric;
+
+ [EventSource(Name = "MyCompany-Arcadia.Assistant.SF-Arcadia.Assistant.Sharepoint")]
+ internal sealed class ServiceEventSource : EventSource
+ {
+ public static readonly ServiceEventSource Current = new ServiceEventSource();
+
+ // Instance constructor is private to enforce singleton semantics
+ private ServiceEventSource()
+ {
+ }
+
+ #region Keywords
+
+ // Event keywords can be used to categorize events.
+ // Each keyword is a bit flag. A single event can be associated with multiple keywords (via EventAttribute.Keywords property).
+ // Keywords must be defined as a public class named 'Keywords' inside EventSource that uses them.
+ public static class Keywords
+ {
+ public const EventKeywords Requests = (EventKeywords)0x1L;
+ public const EventKeywords ServiceInitialization = (EventKeywords)0x2L;
+ }
+
+ #endregion
+
+ #region Events
+
+ // Define an instance method for each event you want to record and apply an [Event] attribute to it.
+ // The method name is the name of the event.
+ // Pass any parameters you want to record with the event (only primitive integer types, DateTime, Guid & string are allowed).
+ // Each event method implementation should check whether the event source is enabled, and if it is, call WriteEvent() method to raise the event.
+ // The number and types of arguments passed to every event method must exactly match what is passed to WriteEvent().
+ // Put [NonEvent] attribute on all methods that do not define an event.
+ // For more information see https://msdn.microsoft.com/en-us/library/system.diagnostics.tracing.eventsource.aspx
+
+ [NonEvent]
+ public void Message(string message, params object[] args)
+ {
+ if (this.IsEnabled())
+ {
+ var finalMessage = string.Format(message, args);
+ this.Message(finalMessage);
+ }
+ }
+
+ private const int MessageEventId = 1;
+
+ [Event(MessageEventId, Level = EventLevel.Informational, Message = "{0}")]
+ public void Message(string message)
+ {
+ if (this.IsEnabled())
+ {
+ this.WriteEvent(MessageEventId, message);
+ }
+ }
+
+ [NonEvent]
+ public void ServiceMessage(StatelessServiceContext serviceContext, string message, params object[] args)
+ {
+ if (this.IsEnabled())
+ {
+ var finalMessage = string.Format(message, args);
+ this.ServiceMessage(
+ serviceContext.ServiceName.ToString(),
+ serviceContext.ServiceTypeName,
+ serviceContext.InstanceId,
+ serviceContext.PartitionId,
+ serviceContext.CodePackageActivationContext.ApplicationName,
+ serviceContext.CodePackageActivationContext.ApplicationTypeName,
+ serviceContext.NodeContext.NodeName,
+ finalMessage);
+ }
+ }
+
+ // For very high-frequency events it might be advantageous to raise events using WriteEventCore API.
+ // This results in more efficient parameter handling, but requires explicit allocation of EventData structure and unsafe code.
+ // To enable this code path, define UNSAFE conditional compilation symbol and turn on unsafe code support in project properties.
+ private const int ServiceMessageEventId = 2;
+
+ [Event(ServiceMessageEventId, Level = EventLevel.Informational, Message = "{7}")]
+ private
+#if UNSAFE
+ unsafe
+#endif
+ void ServiceMessage(
+ string serviceName,
+ string serviceTypeName,
+ long replicaOrInstanceId,
+ Guid partitionId,
+ string applicationName,
+ string applicationTypeName,
+ string nodeName,
+ string message)
+ {
+#if !UNSAFE
+ this.WriteEvent(ServiceMessageEventId, serviceName, serviceTypeName, replicaOrInstanceId, partitionId, applicationName, applicationTypeName, nodeName, message);
+#else
+ const int numArgs = 8;
+ fixed (char* pServiceName = serviceName, pServiceTypeName = serviceTypeName, pApplicationName = applicationName, pApplicationTypeName = applicationTypeName, pNodeName = nodeName, pMessage = message)
+ {
+ EventData* eventData = stackalloc EventData[numArgs];
+ eventData[0] = new EventData { DataPointer = (IntPtr) pServiceName, Size = SizeInBytes(serviceName) };
+ eventData[1] = new EventData { DataPointer = (IntPtr) pServiceTypeName, Size = SizeInBytes(serviceTypeName) };
+ eventData[2] = new EventData { DataPointer = (IntPtr) (&replicaOrInstanceId), Size = sizeof(long) };
+ eventData[3] = new EventData { DataPointer = (IntPtr) (&partitionId), Size = sizeof(Guid) };
+ eventData[4] = new EventData { DataPointer = (IntPtr) pApplicationName, Size = SizeInBytes(applicationName) };
+ eventData[5] = new EventData { DataPointer = (IntPtr) pApplicationTypeName, Size = SizeInBytes(applicationTypeName) };
+ eventData[6] = new EventData { DataPointer = (IntPtr) pNodeName, Size = SizeInBytes(nodeName) };
+ eventData[7] = new EventData { DataPointer = (IntPtr) pMessage, Size = SizeInBytes(message) };
+
+ WriteEventCore(ServiceMessageEventId, numArgs, eventData);
+ }
+#endif
+ }
+
+ private const int ServiceTypeRegisteredEventId = 3;
+
+ [Event(ServiceTypeRegisteredEventId, Level = EventLevel.Informational, Message = "Service host process {0} registered service type {1}", Keywords = Keywords.ServiceInitialization)]
+ public void ServiceTypeRegistered(int hostProcessId, string serviceType)
+ {
+ this.WriteEvent(ServiceTypeRegisteredEventId, hostProcessId, serviceType);
+ }
+
+ private const int ServiceHostInitializationFailedEventId = 4;
+
+ [Event(ServiceHostInitializationFailedEventId, Level = EventLevel.Error, Message = "Service host initialization failed", Keywords = Keywords.ServiceInitialization)]
+ public void ServiceHostInitializationFailed(string exception)
+ {
+ this.WriteEvent(ServiceHostInitializationFailedEventId, exception);
+ }
+
+ // A pair of events sharing the same name prefix with a "Start"/"Stop" suffix implicitly marks boundaries of an event tracing activity.
+ // These activities can be automatically picked up by debugging and profiling tools, which can compute their execution time, child activities,
+ // and other statistics.
+ private const int ServiceRequestStartEventId = 5;
+
+ [Event(ServiceRequestStartEventId, Level = EventLevel.Informational, Message = "Service request '{0}' started", Keywords = Keywords.Requests)]
+ public void ServiceRequestStart(string requestTypeName)
+ {
+ this.WriteEvent(ServiceRequestStartEventId, requestTypeName);
+ }
+
+ private const int ServiceRequestStopEventId = 6;
+
+ [Event(ServiceRequestStopEventId, Level = EventLevel.Informational, Message = "Service request '{0}' finished", Keywords = Keywords.Requests)]
+ public void ServiceRequestStop(string requestTypeName, string exception = "")
+ {
+ this.WriteEvent(ServiceRequestStopEventId, requestTypeName, exception);
+ }
+
+ #endregion
+
+ #region Private methods
+
+#if UNSAFE
+ private int SizeInBytes(string s)
+ {
+ if (s == null)
+ {
+ return 0;
+ }
+ else
+ {
+ return (s.Length + 1) * sizeof(char);
+ }
+ }
+#endif
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
new file mode 100644
index 000000000..94e2ed709
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
@@ -0,0 +1,52 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Fabric;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ using Microsoft.ServiceFabric.Services.Communication.Runtime;
+ using Microsoft.ServiceFabric.Services.Runtime;
+
+ ///
+ /// An instance of this class is created for each service instance by the Service Fabric runtime.
+ ///
+ internal sealed class Sharepoint : StatelessService
+ {
+ public Sharepoint(StatelessServiceContext context)
+ : base(context)
+ {
+ }
+
+ ///
+ /// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
+ ///
+ /// A collection of listeners.
+ protected override IEnumerable CreateServiceInstanceListeners()
+ {
+ return new ServiceInstanceListener[0];
+ }
+
+ ///
+ /// This is the main entry point for your service instance.
+ ///
+ /// Canceled when Service Fabric needs to shut down this service instance.
+ protected override async Task RunAsync(CancellationToken cancellationToken)
+ {
+ // TODO: Replace the following sample code with your own logic
+ // or remove this RunAsync override if it's not needed in your service.
+
+ long iterations = 0;
+
+ while (true)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
+
+ await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.sln b/server2/Arcadia.Assistant/Arcadia.Assistant.sln
index bf3a7bff8..ba512d951 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.sln
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.sln
@@ -67,130 +67,278 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.MobileBui
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "MobileBuilds", "MobileBuilds", "{13E39FC7-D22B-4521-B65A-48C62593821F}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sharepoint", "Sharepoint", "{EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.ExternalStorages.Abstractions", "Arcadia.Assistant.ExternalStorages.Abstractions\Arcadia.Assistant.ExternalStorages.Abstractions.csproj", "{9507586E-6D63-432E-99E2-59B73907ACA0}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.ExternalStorages.SharepointOnline", "Arcadia.Assistant.ExternalStorages.SharepointOnline\Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj", "{085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.Sharepoint", "Arcadia.Assistant.Sharepoint\Arcadia.Assistant.Sharepoint.csproj", "{C5818540-DECF-409D-82CE-D34E72C34D7D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
+ Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {80714C33-E9A0-4D12-A77F-A86574998240}.Debug|Any CPU.ActiveCfg = Debug|x64
{80714C33-E9A0-4D12-A77F-A86574998240}.Debug|x64.ActiveCfg = Debug|x64
{80714C33-E9A0-4D12-A77F-A86574998240}.Debug|x64.Build.0 = Debug|x64
{80714C33-E9A0-4D12-A77F-A86574998240}.Debug|x64.Deploy.0 = Debug|x64
+ {80714C33-E9A0-4D12-A77F-A86574998240}.Release|Any CPU.ActiveCfg = Release|x64
{80714C33-E9A0-4D12-A77F-A86574998240}.Release|x64.ActiveCfg = Release|x64
{80714C33-E9A0-4D12-A77F-A86574998240}.Release|x64.Build.0 = Release|x64
{80714C33-E9A0-4D12-A77F-A86574998240}.Release|x64.Deploy.0 = Release|x64
+ {7A1EA298-4475-4086-9952-6D35B5BEB944}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7A1EA298-4475-4086-9952-6D35B5BEB944}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7A1EA298-4475-4086-9952-6D35B5BEB944}.Debug|x64.ActiveCfg = Debug|Any CPU
{7A1EA298-4475-4086-9952-6D35B5BEB944}.Debug|x64.Build.0 = Debug|Any CPU
+ {7A1EA298-4475-4086-9952-6D35B5BEB944}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7A1EA298-4475-4086-9952-6D35B5BEB944}.Release|Any CPU.Build.0 = Release|Any CPU
{7A1EA298-4475-4086-9952-6D35B5BEB944}.Release|x64.ActiveCfg = Release|Any CPU
{7A1EA298-4475-4086-9952-6D35B5BEB944}.Release|x64.Build.0 = Release|Any CPU
+ {7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Debug|x64.ActiveCfg = Debug|x64
{7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Debug|x64.Build.0 = Debug|x64
+ {7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Release|Any CPU.Build.0 = Release|Any CPU
{7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Release|x64.ActiveCfg = Release|x64
{7C909B53-A5FE-4F3B-BCFC-E707FD89955B}.Release|x64.Build.0 = Release|x64
+ {2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Debug|x64.ActiveCfg = Debug|Any CPU
{2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Debug|x64.Build.0 = Debug|Any CPU
+ {2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Release|Any CPU.Build.0 = Release|Any CPU
{2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Release|x64.ActiveCfg = Release|Any CPU
{2D76AE9B-BCEE-40A3-BB5C-61E34624EBC2}.Release|x64.Build.0 = Release|Any CPU
+ {493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Debug|Any CPU.Build.0 = Debug|Any CPU
{493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Debug|x64.ActiveCfg = Debug|Any CPU
{493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Debug|x64.Build.0 = Debug|Any CPU
+ {493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Release|Any CPU.Build.0 = Release|Any CPU
{493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Release|x64.ActiveCfg = Release|Any CPU
{493E9E0F-4D9F-4283-A00D-937E9CD23C13}.Release|x64.Build.0 = Release|Any CPU
+ {137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Debug|Any CPU.Build.0 = Debug|Any CPU
{137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Debug|x64.ActiveCfg = Debug|Any CPU
{137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Debug|x64.Build.0 = Debug|Any CPU
+ {137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Release|Any CPU.Build.0 = Release|Any CPU
{137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Release|x64.ActiveCfg = Release|Any CPU
{137B9BB9-CFB5-4448-B930-DBE5DFE8D198}.Release|x64.Build.0 = Release|Any CPU
+ {BF24FE61-7804-4415-913B-B8714633B8DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BF24FE61-7804-4415-913B-B8714633B8DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF24FE61-7804-4415-913B-B8714633B8DF}.Debug|x64.ActiveCfg = Debug|x64
{BF24FE61-7804-4415-913B-B8714633B8DF}.Debug|x64.Build.0 = Debug|x64
+ {BF24FE61-7804-4415-913B-B8714633B8DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BF24FE61-7804-4415-913B-B8714633B8DF}.Release|Any CPU.Build.0 = Release|Any CPU
{BF24FE61-7804-4415-913B-B8714633B8DF}.Release|x64.ActiveCfg = Release|Any CPU
{BF24FE61-7804-4415-913B-B8714633B8DF}.Release|x64.Build.0 = Release|Any CPU
+ {5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Debug|x64.ActiveCfg = Debug|Any CPU
{5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Debug|x64.Build.0 = Debug|Any CPU
+ {5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Release|Any CPU.Build.0 = Release|Any CPU
{5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Release|x64.ActiveCfg = Release|Any CPU
{5D2D5D76-743D-4CE4-BA71-B5F0793FB330}.Release|x64.Build.0 = Release|Any CPU
+ {287D4A21-F212-4C65-A2A0-8806DA0846A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {287D4A21-F212-4C65-A2A0-8806DA0846A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{287D4A21-F212-4C65-A2A0-8806DA0846A9}.Debug|x64.ActiveCfg = Debug|Any CPU
{287D4A21-F212-4C65-A2A0-8806DA0846A9}.Debug|x64.Build.0 = Debug|Any CPU
+ {287D4A21-F212-4C65-A2A0-8806DA0846A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {287D4A21-F212-4C65-A2A0-8806DA0846A9}.Release|Any CPU.Build.0 = Release|Any CPU
{287D4A21-F212-4C65-A2A0-8806DA0846A9}.Release|x64.ActiveCfg = Release|Any CPU
{287D4A21-F212-4C65-A2A0-8806DA0846A9}.Release|x64.Build.0 = Release|Any CPU
+ {D773B729-E46C-432D-92B5-3EA7F352FBFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D773B729-E46C-432D-92B5-3EA7F352FBFD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D773B729-E46C-432D-92B5-3EA7F352FBFD}.Debug|x64.ActiveCfg = Debug|x64
{D773B729-E46C-432D-92B5-3EA7F352FBFD}.Debug|x64.Build.0 = Debug|x64
+ {D773B729-E46C-432D-92B5-3EA7F352FBFD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D773B729-E46C-432D-92B5-3EA7F352FBFD}.Release|Any CPU.Build.0 = Release|Any CPU
{D773B729-E46C-432D-92B5-3EA7F352FBFD}.Release|x64.ActiveCfg = Release|Any CPU
{D773B729-E46C-432D-92B5-3EA7F352FBFD}.Release|x64.Build.0 = Release|Any CPU
+ {F4578998-0B4F-49BF-A485-6A11A3D7D837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F4578998-0B4F-49BF-A485-6A11A3D7D837}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F4578998-0B4F-49BF-A485-6A11A3D7D837}.Debug|x64.ActiveCfg = Debug|Any CPU
{F4578998-0B4F-49BF-A485-6A11A3D7D837}.Debug|x64.Build.0 = Debug|Any CPU
+ {F4578998-0B4F-49BF-A485-6A11A3D7D837}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F4578998-0B4F-49BF-A485-6A11A3D7D837}.Release|Any CPU.Build.0 = Release|Any CPU
{F4578998-0B4F-49BF-A485-6A11A3D7D837}.Release|x64.ActiveCfg = Release|Any CPU
{F4578998-0B4F-49BF-A485-6A11A3D7D837}.Release|x64.Build.0 = Release|Any CPU
+ {900D57EF-6192-4825-9DED-6A81840BB8DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {900D57EF-6192-4825-9DED-6A81840BB8DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{900D57EF-6192-4825-9DED-6A81840BB8DA}.Debug|x64.ActiveCfg = Debug|x64
{900D57EF-6192-4825-9DED-6A81840BB8DA}.Debug|x64.Build.0 = Debug|x64
+ {900D57EF-6192-4825-9DED-6A81840BB8DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {900D57EF-6192-4825-9DED-6A81840BB8DA}.Release|Any CPU.Build.0 = Release|Any CPU
{900D57EF-6192-4825-9DED-6A81840BB8DA}.Release|x64.ActiveCfg = Release|Any CPU
{900D57EF-6192-4825-9DED-6A81840BB8DA}.Release|x64.Build.0 = Release|Any CPU
+ {C9DF5143-8E11-4731-B76E-B187B0A95444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C9DF5143-8E11-4731-B76E-B187B0A95444}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9DF5143-8E11-4731-B76E-B187B0A95444}.Debug|x64.ActiveCfg = Debug|Any CPU
{C9DF5143-8E11-4731-B76E-B187B0A95444}.Debug|x64.Build.0 = Debug|Any CPU
+ {C9DF5143-8E11-4731-B76E-B187B0A95444}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C9DF5143-8E11-4731-B76E-B187B0A95444}.Release|Any CPU.Build.0 = Release|Any CPU
{C9DF5143-8E11-4731-B76E-B187B0A95444}.Release|x64.ActiveCfg = Release|Any CPU
{C9DF5143-8E11-4731-B76E-B187B0A95444}.Release|x64.Build.0 = Release|Any CPU
+ {1877FF11-485D-490B-9409-1AA57C8EBB3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1877FF11-485D-490B-9409-1AA57C8EBB3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1877FF11-485D-490B-9409-1AA57C8EBB3D}.Debug|x64.ActiveCfg = Debug|x64
{1877FF11-485D-490B-9409-1AA57C8EBB3D}.Debug|x64.Build.0 = Debug|x64
+ {1877FF11-485D-490B-9409-1AA57C8EBB3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1877FF11-485D-490B-9409-1AA57C8EBB3D}.Release|Any CPU.Build.0 = Release|Any CPU
{1877FF11-485D-490B-9409-1AA57C8EBB3D}.Release|x64.ActiveCfg = Release|Any CPU
{1877FF11-485D-490B-9409-1AA57C8EBB3D}.Release|x64.Build.0 = Release|Any CPU
+ {C228C471-6C35-4630-B475-3161A800C72A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C228C471-6C35-4630-B475-3161A800C72A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C228C471-6C35-4630-B475-3161A800C72A}.Debug|x64.ActiveCfg = Debug|Any CPU
{C228C471-6C35-4630-B475-3161A800C72A}.Debug|x64.Build.0 = Debug|Any CPU
+ {C228C471-6C35-4630-B475-3161A800C72A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C228C471-6C35-4630-B475-3161A800C72A}.Release|Any CPU.Build.0 = Release|Any CPU
{C228C471-6C35-4630-B475-3161A800C72A}.Release|x64.ActiveCfg = Release|Any CPU
{C228C471-6C35-4630-B475-3161A800C72A}.Release|x64.Build.0 = Release|Any CPU
+ {A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Debug|x64.ActiveCfg = Debug|x64
{A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Debug|x64.Build.0 = Debug|x64
+ {A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Release|Any CPU.Build.0 = Release|Any CPU
{A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Release|x64.ActiveCfg = Release|Any CPU
{A911FABD-DDEA-4B78-BAFB-6CE60BD9D772}.Release|x64.Build.0 = Release|Any CPU
+ {E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Debug|x64.ActiveCfg = Debug|Any CPU
{E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Debug|x64.Build.0 = Debug|Any CPU
+ {E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Release|Any CPU.Build.0 = Release|Any CPU
{E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Release|x64.ActiveCfg = Release|Any CPU
{E3B5A256-2924-4E8C-A798-0D74615C2BB6}.Release|x64.Build.0 = Release|Any CPU
+ {7925064E-3C64-4865-B603-DEAF82E6A4AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7925064E-3C64-4865-B603-DEAF82E6A4AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7925064E-3C64-4865-B603-DEAF82E6A4AC}.Debug|x64.ActiveCfg = Debug|x64
{7925064E-3C64-4865-B603-DEAF82E6A4AC}.Debug|x64.Build.0 = Debug|x64
+ {7925064E-3C64-4865-B603-DEAF82E6A4AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7925064E-3C64-4865-B603-DEAF82E6A4AC}.Release|Any CPU.Build.0 = Release|Any CPU
{7925064E-3C64-4865-B603-DEAF82E6A4AC}.Release|x64.ActiveCfg = Release|Any CPU
{7925064E-3C64-4865-B603-DEAF82E6A4AC}.Release|x64.Build.0 = Release|Any CPU
+ {B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Debug|x64.ActiveCfg = Debug|Any CPU
{B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Debug|x64.Build.0 = Debug|Any CPU
+ {B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Release|Any CPU.Build.0 = Release|Any CPU
{B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Release|x64.ActiveCfg = Release|Any CPU
{B78E8874-6AA6-47A2-81BC-7CD9D110CF10}.Release|x64.Build.0 = Release|Any CPU
+ {B66BE3BE-43A5-47D4-989C-0B905BED8415}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B66BE3BE-43A5-47D4-989C-0B905BED8415}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B66BE3BE-43A5-47D4-989C-0B905BED8415}.Debug|x64.ActiveCfg = Debug|x64
{B66BE3BE-43A5-47D4-989C-0B905BED8415}.Debug|x64.Build.0 = Debug|x64
+ {B66BE3BE-43A5-47D4-989C-0B905BED8415}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B66BE3BE-43A5-47D4-989C-0B905BED8415}.Release|Any CPU.Build.0 = Release|Any CPU
{B66BE3BE-43A5-47D4-989C-0B905BED8415}.Release|x64.ActiveCfg = Release|Any CPU
{B66BE3BE-43A5-47D4-989C-0B905BED8415}.Release|x64.Build.0 = Release|Any CPU
+ {04C572F0-0075-4A03-9201-247E610B5BB5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {04C572F0-0075-4A03-9201-247E610B5BB5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{04C572F0-0075-4A03-9201-247E610B5BB5}.Debug|x64.ActiveCfg = Debug|Any CPU
{04C572F0-0075-4A03-9201-247E610B5BB5}.Debug|x64.Build.0 = Debug|Any CPU
+ {04C572F0-0075-4A03-9201-247E610B5BB5}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {04C572F0-0075-4A03-9201-247E610B5BB5}.Release|Any CPU.Build.0 = Release|Any CPU
{04C572F0-0075-4A03-9201-247E610B5BB5}.Release|x64.ActiveCfg = Release|Any CPU
{04C572F0-0075-4A03-9201-247E610B5BB5}.Release|x64.Build.0 = Release|Any CPU
+ {0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Debug|x64.ActiveCfg = Debug|x64
{0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Debug|x64.Build.0 = Debug|x64
+ {0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Release|Any CPU.Build.0 = Release|Any CPU
{0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Release|x64.ActiveCfg = Release|Any CPU
{0FE483A6-B5C9-40D4-94A5-13F9006F420C}.Release|x64.Build.0 = Release|Any CPU
+ {88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Debug|x64.ActiveCfg = Debug|Any CPU
{88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Debug|x64.Build.0 = Debug|Any CPU
+ {88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Release|Any CPU.Build.0 = Release|Any CPU
{88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Release|x64.ActiveCfg = Release|Any CPU
{88DA2A02-B4E5-46AF-AE5D-E2405E3AFB5F}.Release|x64.Build.0 = Release|Any CPU
+ {BBA7563D-DFD2-4964-8FD0-CD340B272662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BBA7563D-DFD2-4964-8FD0-CD340B272662}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BBA7563D-DFD2-4964-8FD0-CD340B272662}.Debug|x64.ActiveCfg = Debug|x64
{BBA7563D-DFD2-4964-8FD0-CD340B272662}.Debug|x64.Build.0 = Debug|x64
+ {BBA7563D-DFD2-4964-8FD0-CD340B272662}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BBA7563D-DFD2-4964-8FD0-CD340B272662}.Release|Any CPU.Build.0 = Release|Any CPU
{BBA7563D-DFD2-4964-8FD0-CD340B272662}.Release|x64.ActiveCfg = Release|Any CPU
{BBA7563D-DFD2-4964-8FD0-CD340B272662}.Release|x64.Build.0 = Release|Any CPU
+ {62740673-4B3F-4093-A7E5-94251B7C74EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {62740673-4B3F-4093-A7E5-94251B7C74EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62740673-4B3F-4093-A7E5-94251B7C74EE}.Debug|x64.ActiveCfg = Debug|Any CPU
{62740673-4B3F-4093-A7E5-94251B7C74EE}.Debug|x64.Build.0 = Debug|Any CPU
+ {62740673-4B3F-4093-A7E5-94251B7C74EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {62740673-4B3F-4093-A7E5-94251B7C74EE}.Release|Any CPU.Build.0 = Release|Any CPU
{62740673-4B3F-4093-A7E5-94251B7C74EE}.Release|x64.ActiveCfg = Release|Any CPU
{62740673-4B3F-4093-A7E5-94251B7C74EE}.Release|x64.Build.0 = Release|Any CPU
+ {F402EF81-854B-483C-A795-0FA2796CF153}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F402EF81-854B-483C-A795-0FA2796CF153}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F402EF81-854B-483C-A795-0FA2796CF153}.Debug|x64.ActiveCfg = Debug|x64
{F402EF81-854B-483C-A795-0FA2796CF153}.Debug|x64.Build.0 = Debug|x64
+ {F402EF81-854B-483C-A795-0FA2796CF153}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F402EF81-854B-483C-A795-0FA2796CF153}.Release|Any CPU.Build.0 = Release|Any CPU
{F402EF81-854B-483C-A795-0FA2796CF153}.Release|x64.ActiveCfg = Release|Any CPU
{F402EF81-854B-483C-A795-0FA2796CF153}.Release|x64.Build.0 = Release|Any CPU
+ {BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Debug|x64.ActiveCfg = Debug|Any CPU
{BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Debug|x64.Build.0 = Debug|Any CPU
+ {BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Release|Any CPU.Build.0 = Release|Any CPU
{BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Release|x64.ActiveCfg = Release|Any CPU
{BA73D362-4E99-42F2-9966-E6692B0CBF7D}.Release|x64.Build.0 = Release|Any CPU
+ {BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Debug|x64.ActiveCfg = Debug|Any CPU
{BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Debug|x64.Build.0 = Debug|Any CPU
+ {BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Release|Any CPU.Build.0 = Release|Any CPU
{BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Release|x64.ActiveCfg = Release|Any CPU
{BC44E3D2-B3B0-49F8-965E-25AAB4589BAA}.Release|x64.Build.0 = Release|Any CPU
+ {1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Debug|x64.ActiveCfg = Debug|x64
{1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Debug|x64.Build.0 = Debug|x64
+ {1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Release|Any CPU.Build.0 = Release|Any CPU
{1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Release|x64.ActiveCfg = Release|Any CPU
{1C3C2A57-B9EE-45E6-8639-7295AFDEC518}.Release|x64.Build.0 = Release|Any CPU
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Debug|x64.ActiveCfg = Debug|x64
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Debug|x64.Build.0 = Debug|x64
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Release|x64.ActiveCfg = Release|Any CPU
+ {9507586E-6D63-432E-99E2-59B73907ACA0}.Release|x64.Build.0 = Release|Any CPU
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Debug|x64.ActiveCfg = Debug|x64
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Debug|x64.Build.0 = Debug|x64
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Release|Any CPU.Build.0 = Release|Any CPU
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Release|x64.ActiveCfg = Release|Any CPU
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA}.Release|x64.Build.0 = Release|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Debug|x64.ActiveCfg = Debug|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Debug|x64.Build.0 = Debug|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|x64.ActiveCfg = Release|Any CPU
+ {C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -212,6 +360,10 @@ Global
{BA73D362-4E99-42F2-9966-E6692B0CBF7D} = {13E39FC7-D22B-4521-B65A-48C62593821F}
{BC44E3D2-B3B0-49F8-965E-25AAB4589BAA} = {13E39FC7-D22B-4521-B65A-48C62593821F}
{1C3C2A57-B9EE-45E6-8639-7295AFDEC518} = {13E39FC7-D22B-4521-B65A-48C62593821F}
+ {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90} = {EC5FA305-0A2E-4985-A977-B9C6F2ADEFFF}
+ {9507586E-6D63-432E-99E2-59B73907ACA0} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
+ {085366A3-3D08-45B5-9A24-DDF3E1EEB2FA} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
+ {C5818540-DECF-409D-82CE-D34E72C34D7D} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C376673D-F593-42B1-8093-D1F667D586B1}
From 5268d6df7e16c2fa1c4efa56856a6c09b767b775 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Thu, 5 Dec 2019 18:27:12 +0300
Subject: [PATCH 02/14] Sharepoint syncronization service stage 1
---
...t.ExternalStorages.SharepointOnline.csproj | 5 +--
.../Contracts/SharepointRequest.cs | 5 ++-
.../SharepointAuthTokenService.cs | 7 ++--
.../SharepointOnlineConfiguration.cs | 15 ++++++--
.../SharepointRequestExecutor.cs | 5 ++-
.../ApplicationManifest.xml | 27 ++++++++++++++
.../Arcadia.Assistant.SF.sfproj | 1 +
.../Arcadia.Assistant.Sharepoint.csproj | 13 +++++--
...ISharepointDepartmentsCalendarsSettings.cs | 9 +++++
.../PackageRoot/Config/Settings.xml | 12 ++++---
.../Arcadia.Assistant.Sharepoint/Program.cs | 35 +++++++++++++------
.../Sharepoint.cs | 11 ++++--
.../SharepointDepartmentCalendarMapping.cs | 13 +++++++
.../SharepointDepartmentsCalendarsSettings.cs | 29 +++++++++++++++
14 files changed, 153 insertions(+), 34 deletions(-)
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentCalendarMapping.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj
index 63ca4a47c..6ede2f97b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Arcadia.Assistant.ExternalStorages.SharepointOnline.csproj
@@ -8,9 +8,10 @@
-
+
+
-
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
index abbae1b1e..46980fdb1 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
@@ -4,8 +4,7 @@
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
-
- using Newtonsoft.Json;
+ using System.Text.Json;
public class SharepointRequest
{
@@ -51,7 +50,7 @@ public SharepointRequest WithBearerAuthorizationHeader(string value)
public SharepointRequest WithContent(object content)
{
- var contentString = JsonConvert.SerializeObject(content);
+ var contentString = JsonSerializer.Serialize(content);
this.Content = new StringContent(contentString);
this.AddHeader(ContentTypeHeaderName, "application/json;odata=verbose");
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
index 7beea5d0a..22d317554 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
@@ -8,14 +8,13 @@
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Serialization;
+ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Contracts;
- using Newtonsoft.Json;
-
public class SharepointAuthTokenService : ISharepointAuthTokenService
{
private const string SharePointPrincipal = "00000003-0000-0ff1-ce00-000000000000";
@@ -142,7 +141,7 @@ private async Task GetRealm(string urlSharePoint, CancellationToken canc
var responseContent = await response.Content.ReadAsStringAsync();
- var oauthResult = JsonConvert.DeserializeObject(responseContent);
+ var oauthResult = JsonSerializer.Deserialize(responseContent);
return oauthResult.AccessToken;
}
@@ -166,7 +165,7 @@ private async Task GetMetadataDocument(string realm, Cance
var response = await this.httpClient.GetAsync(metadataEndpointUrl, cancellationToken);
var metadataString = await response.Content.ReadAsStringAsync();
- var metadata = JsonConvert.DeserializeObject(metadataString);
+ var metadata = JsonSerializer.Deserialize(metadataString);
if (metadata == null)
{
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
index 6558169b7..c16780132 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
@@ -1,13 +1,22 @@
namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
{
+ using System.Fabric.Description;
+
using Contracts;
public class SharepointOnlineConfiguration : ISharepointOnlineConfiguration
{
- public string ServerUrl { get; set; } = string.Empty;
+ public SharepointOnlineConfiguration(ConfigurationSection configurationSection)
+ {
+ this.ServerUrl = configurationSection.Parameters["ServerUrl"].Value;
+ this.ClientId = configurationSection.Parameters["ClientId"].Value;
+ this.ClientSecret = configurationSection.Parameters["ClientSecret"].Value;
+ }
+
+ public string ServerUrl { get; set; }
- public string ClientId { get; set; } = string.Empty;
+ public string ClientId { get; set; }
- public string ClientSecret { get; set; } = string.Empty;
+ public string ClientSecret { get; set; }
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs
index b62f1e703..7af733b1e 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointRequestExecutor.cs
@@ -1,13 +1,12 @@
namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
{
using System.Net.Http;
+ using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Contracts;
- using Newtonsoft.Json;
-
public class SharepointRequestExecutor : ISharepointRequestExecutor
{
private readonly ISharepointAuthTokenService authTokenService;
@@ -36,7 +35,7 @@ public async Task ExecuteSharepointRequest(SharepointRequest request, Canc
}
var content = await response.Content.ReadAsStringAsync();
- return JsonConvert.DeserializeObject(content);
+ return JsonSerializer.Deserialize(content);
}
public async Task ExecuteSharepointRequest(SharepointRequest request, CancellationToken cancellationToken = default)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
index 75261691d..4d2162eb4 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
@@ -1,6 +1,7 @@
+
@@ -28,6 +29,10 @@
+
+
+
+
@@ -51,6 +56,23 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -214,6 +236,11 @@
ServiceFabric PowerShell module.
The attribute ServiceTypeName below must match the name defined in the imported ServiceManifest.xml file. -->
+
+
+
+
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/Arcadia.Assistant.SF.sfproj b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/Arcadia.Assistant.SF.sfproj
index 8c8a2e614..bb08eb38d 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/Arcadia.Assistant.SF.sfproj
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/Arcadia.Assistant.SF.sfproj
@@ -36,6 +36,7 @@
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
index 1725162b7..7534eeb15 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
@@ -1,4 +1,4 @@
-
+
Exe
@@ -7,10 +7,19 @@
True
win7-x64
False
+ enable
-
+
+
+
+
+
+
+
+
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs
new file mode 100644
index 000000000..acf7a057a
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs
@@ -0,0 +1,9 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System.Collections.Generic;
+
+ public interface ISharepointDepartmentsCalendarsSettings
+ {
+ IEnumerable DepartmentsCalendars { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
index aea0ce165..cae3cb17e 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
@@ -2,10 +2,12 @@
-
-
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
index e0ce85202..fbd57528b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
@@ -2,9 +2,17 @@ namespace Arcadia.Assistant.Sharepoint
{
using System;
using System.Diagnostics;
+ using System.Fabric;
using System.Threading;
- using Microsoft.ServiceFabric.Services.Runtime;
+ using Autofac;
+ using Autofac.Extensions.DependencyInjection;
+ using Autofac.Integration.ServiceFabric;
+
+ using ExternalStorages.SharepointOnline;
+ using ExternalStorages.SharepointOnline.Contracts;
+
+ using Microsoft.Extensions.DependencyInjection;
internal static class Program
{
@@ -15,18 +23,25 @@ private static void Main()
{
try
{
- // The ServiceManifest.XML file defines one or more service type names.
- // Registering a service maps a service type name to a .NET type.
- // When Service Fabric creates an instance of this service type,
- // an instance of the class is created in this host process.
+ var configurationPackage = FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config");
+
+ var services = new ServiceCollection();
+ services.AddHttpClient();
- ServiceRuntime.RegisterServiceAsync("Arcadia.Assistant.SharepointType",
- context => new Sharepoint(context)).GetAwaiter().GetResult();
+ var builder = new ContainerBuilder();
+ builder.RegisterServiceFabricSupport();
+ builder.Register(x => new SharepointOnlineConfiguration(configurationPackage.Settings.Sections["Sharepoint"])).As().SingleInstance();
+ builder.Register(x => new SharepointDepartmentsCalendarsSettings(configurationPackage.Settings.Sections["DepartmentsCalendars"])).As().SingleInstance();
+ builder.RegisterStatelessService("Arcadia.Assistant.SharepointType");
+ builder.Populate(services);
- ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Sharepoint).Name);
+ using (builder.Build())
+ {
+ ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Sharepoint).Name);
- // Prevents this host process from terminating so services keep running.
- Thread.Sleep(Timeout.Infinite);
+ // Prevents this host process from terminating so services keep running.
+ Thread.Sleep(Timeout.Infinite);
+ }
}
catch (Exception e)
{
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
index 94e2ed709..f6fbe5dfa 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
@@ -6,17 +6,24 @@ namespace Arcadia.Assistant.Sharepoint
using System.Threading;
using System.Threading.Tasks;
+ using ExternalStorages.SharepointOnline.Contracts;
+
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
///
/// An instance of this class is created for each service instance by the Service Fabric runtime.
///
- internal sealed class Sharepoint : StatelessService
+ public class Sharepoint : StatelessService
{
- public Sharepoint(StatelessServiceContext context)
+ private readonly ISharepointOnlineConfiguration sharepointConnectSettings;
+ private readonly ISharepointDepartmentsCalendarsSettings departmentsSettings;
+
+ public Sharepoint(StatelessServiceContext context, ISharepointOnlineConfiguration sharepointConnectSettings, ISharepointDepartmentsCalendarsSettings departmentsSettings)
: base(context)
{
+ this.sharepointConnectSettings = sharepointConnectSettings;
+ this.departmentsSettings = departmentsSettings;
}
///
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentCalendarMapping.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentCalendarMapping.cs
new file mode 100644
index 000000000..ffb1e8289
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentCalendarMapping.cs
@@ -0,0 +1,13 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System.Text.Json.Serialization;
+
+ public class SharepointDepartmentCalendarMapping
+ {
+ [JsonPropertyName("id")]
+ public string DepartmentId { get; set; } = string.Empty;
+
+ [JsonPropertyName("name")]
+ public string Calendar { get; set; } = string.Empty;
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
new file mode 100644
index 000000000..8c2450b3e
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.Json;
+
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System.Fabric.Description;
+ using System.Text.Json.Serialization;
+
+ public class SharepointDepartmentsCalendarsSettings : ISharepointDepartmentsCalendarsSettings
+ {
+ public SharepointDepartmentsCalendarsSettings(ConfigurationSection configurationSection)
+ {
+ try
+ {
+ var val = configurationSection.Parameters["Value"].Value;
+ this.DepartmentsCalendars = JsonSerializer.Deserialize>(val);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine(e);
+ throw;
+ }
+ }
+
+ public IEnumerable DepartmentsCalendars { get; set; }
+ }
+}
From 9beb70769dd1680f7ed2253e8aa4eb0dad952ab3 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Thu, 12 Dec 2019 15:28:41 +0300
Subject: [PATCH 03/14] Complete synchronization
---
...dia.Assistant.Calendar.Abstractions.csproj | 12 +
.../CalendarEvent.cs | 38 +++
.../CalendarEventAdditionalDataEntry.cs | 15 +
.../CalendarEventStatuses.cs | 113 ++++++++
.../CalendarEventTypes.cs | 23 ++
.../CspCalendarEventIdParser.cs | 29 ++
.../DatesPeriod.cs | 104 +++++++
.../SickLeaveStatuses.cs | 17 ++
.../VacationStatuses.cs | 23 ++
.../WorkHoursChangeStatuses.cs | 19 ++
.../EmployeeMetadata.cs | 2 +
.../SharepointOnlineConfiguration.cs | 6 +-
.../ApplicationManifest.xml | 6 +
.../Arcadia.Assistant.Sharepoint.csproj | 8 +-
.../ISharepointSynchronizationSettings.cs | 7 +
.../PackageRoot/Config/Settings.xml | 4 +
.../Arcadia.Assistant.Sharepoint/Program.cs | 45 ++-
.../RemoveCalendarEventFromSharepoint.cs | 19 ++
.../Sharepoint.cs | 274 +++++++++++++++++-
.../SharepointDepartmentsCalendarsSettings.cs | 13 +-
.../SharepointStorageItemComparer.cs | 58 ++++
.../SharepointSynchronizationSettings.cs | 16 +
.../StoreCalendarEventToSharepoint.cs | 19 ++
.../ISickLeaves.cs | 3 +
.../SickLeaveDescription.cs | 5 +
.../SickLeaveModelConverter.cs | 3 +
.../SickLeaves.cs | 12 +
.../IWorkHoursCredit.cs | 2 +
.../WorkHoursCredit.cs | 9 +
.../Arcadia.Assistant/Arcadia.Assistant.sln | 11 +
30 files changed, 889 insertions(+), 26 deletions(-)
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointSynchronizationSettings.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointStorageItemComparer.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizationSettings.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj
new file mode 100644
index 000000000..c9840b768
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj
@@ -0,0 +1,12 @@
+
+
+
+ netstandard2.1
+ AnyCPU;x64
+ enable
+
+
+
+
+
+
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs
new file mode 100644
index 000000000..111787f32
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs
@@ -0,0 +1,38 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ using System.Linq;
+
+ public class CalendarEvent
+ {
+ public CalendarEvent(
+ string eventId,
+ string type,
+ DatesPeriod dates,
+ string status,
+ string employeeId,
+ CalendarEventAdditionalDataEntry[]? additionalData = null)
+ {
+ this.EventId = eventId;
+ this.Dates = dates;
+ this.Status = status;
+ this.Type = type;
+ this.EmployeeId = employeeId;
+ this.AdditionalData = additionalData ?? new CalendarEventAdditionalDataEntry[0];
+ this.IsPending = new CalendarEventStatuses().PendingForType(type).Contains(status);
+ }
+
+ public string EventId { get; }
+
+ public DatesPeriod Dates { get; }
+
+ public string Status { get; }
+
+ public string Type { get; }
+
+ public bool IsPending { get; }
+
+ public string EmployeeId { get; }
+
+ public CalendarEventAdditionalDataEntry[] AdditionalData { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs
new file mode 100644
index 000000000..932429b27
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs
@@ -0,0 +1,15 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ public class CalendarEventAdditionalDataEntry
+ {
+ public CalendarEventAdditionalDataEntry(string key, string value)
+ {
+ this.Key = key;
+ this.Value = value;
+ }
+
+ public string Key { get; }
+
+ public string Value { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs
new file mode 100644
index 000000000..e61e5b356
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs
@@ -0,0 +1,113 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ using System.Collections.Generic;
+
+ public class CalendarEventStatuses
+ {
+ private static readonly IReadOnlyDictionary StatusesByType = new Dictionary
+ {
+ { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.All },
+ { CalendarEventTypes.Workout, WorkHoursChangeStatuses.All },
+ { CalendarEventTypes.Sickleave, SickLeaveStatuses.All },
+ { CalendarEventTypes.Vacation, VacationStatuses.All }
+ };
+
+ private static readonly IReadOnlyDictionary PendingStatusesByType = new Dictionary
+ {
+ { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Pending },
+ { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Pending },
+ { CalendarEventTypes.Sickleave, SickLeaveStatuses.Pending },
+ { CalendarEventTypes.Vacation, VacationStatuses.Pending }
+ };
+
+ private static readonly IReadOnlyDictionary ActualStatusesByType = new Dictionary
+ {
+ { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Actual },
+ { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Actual },
+ { CalendarEventTypes.Sickleave, SickLeaveStatuses.Actual },
+ { CalendarEventTypes.Vacation, VacationStatuses.Actual }
+ };
+
+ private static readonly IReadOnlyDictionary ApprovedStatusByType = new Dictionary
+ {
+ { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Approved },
+ { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Approved },
+ { CalendarEventTypes.Vacation, VacationStatuses.Approved }
+ };
+
+ private static readonly IReadOnlyDictionary RejectedStatusByType = new Dictionary
+ {
+ { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Rejected },
+ { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Rejected },
+ { CalendarEventTypes.Vacation, VacationStatuses.Rejected }
+ };
+
+ private static readonly IReadOnlyDictionary CancelledStatusByType = new Dictionary
+ {
+ { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Cancelled },
+ { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Cancelled },
+ { CalendarEventTypes.Sickleave, SickLeaveStatuses.Cancelled },
+ { CalendarEventTypes.Vacation, VacationStatuses.Cancelled }
+ };
+
+ public string[] AllForType(string type)
+ {
+ if (StatusesByType.TryGetValue(type, out var statuses))
+ {
+ return statuses;
+ }
+
+ return new string[0];
+ }
+
+ public string[] PendingForType(string type)
+ {
+ if (PendingStatusesByType.TryGetValue(type, out var statuses))
+ {
+ return statuses;
+ }
+
+ return new string[0];
+ }
+
+ public string[] ActualForType(string type)
+ {
+ if (ActualStatusesByType.TryGetValue(type, out var statuses))
+ {
+ return statuses;
+ }
+
+ return new string[0];
+ }
+
+ public string? ApprovedForType(string type)
+ {
+ if (ApprovedStatusByType.TryGetValue(type, out var status))
+ {
+ return status;
+ }
+
+ return null;
+ }
+
+ public string? RejectedForType(string type)
+ {
+ if (RejectedStatusByType.TryGetValue(type, out var status))
+ {
+ return status;
+ }
+
+ return null;
+ }
+
+ public string? CancelledForType(string type)
+ {
+ if (CancelledStatusByType.TryGetValue(type, out var status))
+ {
+ return status;
+ }
+
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs
new file mode 100644
index 000000000..066fb7ce3
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs
@@ -0,0 +1,23 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ using System;
+ using System.Linq;
+
+ public static class CalendarEventTypes
+ {
+ public const string Vacation = "Vacation";
+
+ public const string Dayoff = "Dayoff";
+
+ public const string Workout = "Workout";
+
+ public const string Sickleave = "Sickleave";
+
+ public static readonly string[] All = { Vacation, Dayoff, Workout, Sickleave };
+
+ public static bool IsKnownType(string x)
+ {
+ return All.Contains(x, StringComparer.InvariantCultureIgnoreCase);
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs
new file mode 100644
index 000000000..7107a2dec
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs
@@ -0,0 +1,29 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ using System;
+
+ public static class CspCalendarEventIdParser
+ {
+ public static int GetCspIdFromCalendarEvent(string calendarEventId, string calendarEventType)
+ {
+ var parts = calendarEventId.Split('_');
+
+ if (parts.Length != 2 || parts[0] != calendarEventType || !int.TryParse(parts[1], out var cspId))
+ {
+ throw new ArgumentException("Calendar event id has wrong format");
+ }
+
+ return cspId;
+ }
+
+ public static string GetCalendarEventIdFromCspId(int cspId, string calendarEventType)
+ {
+ return $"{calendarEventType}_{cspId}";
+ }
+
+ public static string GetCalendarEventIdFromCspId(Guid cspId, string calendarEventType)
+ {
+ return $"{calendarEventType}_{cspId}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs
new file mode 100644
index 000000000..4dbcaaf43
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs
@@ -0,0 +1,104 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ using System;
+
+ public sealed class DatesPeriod
+ {
+ public DatesPeriod(DateTime startDate, DateTime endDate, int startWorkingHour = 0, int finishWorkingHour = 8)
+ {
+ this.StartDate = MinDate(startDate, endDate);
+ this.EndDate = MaxDate(startDate, endDate);
+ this.StartWorkingHour = Math.Min(startWorkingHour, finishWorkingHour);
+ this.FinishWorkingHour = Math.Max(startWorkingHour, finishWorkingHour);
+ }
+
+ public DateTime StartDate { get; }
+
+ public DateTime EndDate { get; }
+
+ ///
+ /// Starting working hour index. Typically, 0 or 4.
+ ///
+ public int StartWorkingHour { get; }
+
+ ///
+ /// Finish working hour index. Typically, 4 or 8
+ ///
+ public int FinishWorkingHour { get; }
+
+ private bool Equals(DatesPeriod other)
+ {
+ return this.StartDate.Equals(other.StartDate)
+ && this.EndDate.Equals(other.EndDate)
+ && this.StartWorkingHour == other.StartWorkingHour
+ && this.FinishWorkingHour == other.FinishWorkingHour;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ if (obj.GetType() != this.GetType())
+ {
+ return false;
+ }
+
+ return this.Equals((DatesPeriod)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = this.StartDate.GetHashCode();
+ hashCode = (hashCode * 397) ^ this.EndDate.GetHashCode();
+ hashCode = (hashCode * 397) ^ this.StartWorkingHour;
+ hashCode = (hashCode * 397) ^ this.FinishWorkingHour;
+ return hashCode;
+ }
+ }
+
+ public static bool operator ==(DatesPeriod left, DatesPeriod right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(DatesPeriod left, DatesPeriod right)
+ {
+ return !Equals(left, right);
+ }
+
+ public bool DatesIntersectsWith(DatesPeriod? period)
+ {
+ if (period == null)
+ {
+ return false;
+ }
+
+ if (this.EndDate < period.StartDate || period.EndDate < this.StartDate)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static DateTime MinDate(DateTime first, DateTime second)
+ {
+ return first <= second ? first : second;
+ }
+
+ private static DateTime MaxDate(DateTime first, DateTime second)
+ {
+ return first >= second ? first : second;
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs
new file mode 100644
index 000000000..e7596fa40
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs
@@ -0,0 +1,17 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ public static class SickLeaveStatuses
+ {
+ public const string Requested = "Requested";
+
+ public const string Cancelled = "Cancelled";
+
+ public const string Completed = "Completed";
+
+ public static readonly string[] All = { Requested, Completed, Cancelled };
+
+ public static readonly string[] Pending = { Requested };
+
+ public static readonly string[] Actual = { Requested, Completed };
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs
new file mode 100644
index 000000000..a55c1e6ee
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs
@@ -0,0 +1,23 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ public static class VacationStatuses
+ {
+ public const string Requested = "Requested";
+
+ public const string Cancelled = "Cancelled";
+
+ public const string Approved = "Approved";
+
+ public const string Rejected = "Rejected";
+
+ public const string AccountingReady = "AccountingReady";
+
+ public const string Processed = "Processed";
+
+ public static readonly string[] All = { Requested, Approved, Cancelled, Rejected, AccountingReady, Processed };
+
+ public static readonly string[] Pending = { Requested };
+
+ public static readonly string[] Actual = { Requested, Approved, AccountingReady, AccountingReady, Processed };
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs
new file mode 100644
index 000000000..f0076ecce
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs
@@ -0,0 +1,19 @@
+namespace Arcadia.Assistant.Calendar.Abstractions
+{
+ public static class WorkHoursChangeStatuses
+ {
+ public const string Requested = "Requested";
+
+ public const string Cancelled = "Cancelled";
+
+ public const string Approved = "Approved";
+
+ public const string Rejected = "Rejected";
+
+ public static readonly string[] All = { Requested, Approved, Cancelled, Rejected };
+
+ public static readonly string[] Pending = { Requested };
+
+ public static readonly string[] Actual = { Requested, Approved };
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs
index 223df6491..d1facefa8 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs
@@ -59,6 +59,8 @@ public EmployeeMetadata(EmployeeId employeeId, string email)
return CalculateYearsFromDate(this.BirthDate, date);
}
+ public string Name => $"{this.FirstName ?? string.Empty} {this.LastName ?? string.Empty}";
+
public int? YearsServedAt(DateTime date)
{
DateTime toDate;
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
index c16780132..99b6e080f 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointOnlineConfiguration.cs
@@ -13,10 +13,10 @@ public SharepointOnlineConfiguration(ConfigurationSection configurationSection)
this.ClientSecret = configurationSection.Parameters["ClientSecret"].Value;
}
- public string ServerUrl { get; set; }
+ public string ServerUrl { get; }
- public string ClientId { get; set; }
+ public string ClientId { get; }
- public string ClientSecret { get; set; }
+ public string ClientSecret { get; }
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
index 4d2162eb4..7d8d57ab8 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
@@ -32,6 +32,8 @@
+
+
@@ -65,6 +67,10 @@
+
+
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
index 7534eeb15..321eebb44 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
@@ -19,7 +19,13 @@
+
+
+
+
+
+
-
+
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointSynchronizationSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointSynchronizationSettings.cs
new file mode 100644
index 000000000..939c4b11a
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointSynchronizationSettings.cs
@@ -0,0 +1,7 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ public interface ISharepointSynchronizationSettings
+ {
+ int SynchronizationIntervalMinutes { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
index cae3cb17e..7fbecade1 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
@@ -6,6 +6,10 @@
+
+
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
index fbd57528b..f386300e1 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
@@ -3,16 +3,29 @@ namespace Arcadia.Assistant.Sharepoint
using System;
using System.Diagnostics;
using System.Fabric;
+ using System.Linq;
using System.Threading;
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Autofac.Integration.ServiceFabric;
+ using Employees.Contracts;
+
+ using ExternalStorages.Abstractions;
using ExternalStorages.SharepointOnline;
using ExternalStorages.SharepointOnline.Contracts;
using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.ServiceFabric.Services.Remoting.Client;
+
+ using Organization.Contracts;
+
+ using SickLeaves.Contracts;
+
+ using Vacations.Contracts;
+
+ using WorkHoursCredit.Contracts;
internal static class Program
{
@@ -24,15 +37,45 @@ private static void Main()
try
{
var configurationPackage = FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config");
+ var sharepointSection = configurationPackage.Settings.Sections["Sharepoint"];
+ var calendarEventIdField = sharepointSection.Parameters.TryGetValue("CalendarEventIdField", out var val) ? val.Value : null;
var services = new ServiceCollection();
services.AddHttpClient();
var builder = new ContainerBuilder();
builder.RegisterServiceFabricSupport();
- builder.Register(x => new SharepointOnlineConfiguration(configurationPackage.Settings.Sections["Sharepoint"])).As().SingleInstance();
+ builder.Register(x => new SharepointOnlineConfiguration(sharepointSection)).As().SingleInstance();
+ builder.Register(x => new SharepointSynchronizationSettings(configurationPackage.Settings.Sections["Service"])).As().SingleInstance();
builder.Register(x => new SharepointDepartmentsCalendarsSettings(configurationPackage.Settings.Sections["DepartmentsCalendars"])).As().SingleInstance();
+ builder
+ .Register(ctx =>
+ {
+ if (string.IsNullOrEmpty(calendarEventIdField))
+ {
+ return new SharepointFieldsMapper();
+ }
+
+ var mapping = SharepointFieldsMapper.DefaultMapping
+ .Union(new[]
+ {
+ SharepointFieldsMapper.CreateMapping(x => x.CalendarEventId, calendarEventIdField)
+ });
+ return new SharepointFieldsMapper(mapping.ToArray());
+ })
+ .As();
+
+ builder.RegisterType().As();
+ builder.RegisterType().As();
+ builder.RegisterType().As();
+ builder.RegisterType().As();
+ builder.RegisterInstance(new ServiceProxyFactory());
builder.RegisterStatelessService("Arcadia.Assistant.SharepointType");
+ builder.RegisterModule();
+ builder.RegisterModule();
+ builder.RegisterModule();
+ builder.RegisterModule();
+ builder.RegisterModule();
builder.Populate(services);
using (builder.Build())
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs
new file mode 100644
index 000000000..dbea220c9
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs
@@ -0,0 +1,19 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using Calendar.Abstractions;
+
+ using Employees.Contracts;
+
+ public class RemoveCalendarEventFromSharepoint
+ {
+ public RemoveCalendarEventFromSharepoint(CalendarEvent @event, EmployeeMetadata employeeMetadata)
+ {
+ this.Event = @event;
+ this.EmployeeMetadata = employeeMetadata;
+ }
+
+ public CalendarEvent Event { get; }
+
+ public EmployeeMetadata EmployeeMetadata { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
index f6fbe5dfa..8aa1b0a25 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
@@ -3,27 +3,62 @@ namespace Arcadia.Assistant.Sharepoint
using System;
using System.Collections.Generic;
using System.Fabric;
+ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
- using ExternalStorages.SharepointOnline.Contracts;
+ using Calendar.Abstractions;
+
+ using Employees.Contracts;
+
+ using ExternalStorages.Abstractions;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
+ using Organization.Contracts;
+
+ using SickLeaves.Contracts;
+
+ using Vacations.Contracts;
+
+ using WorkHoursCredit.Contracts;
+
///
/// An instance of this class is created for each service instance by the Service Fabric runtime.
///
public class Sharepoint : StatelessService
{
- private readonly ISharepointOnlineConfiguration sharepointConnectSettings;
- private readonly ISharepointDepartmentsCalendarsSettings departmentsSettings;
+ private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
+ private readonly IEmployees employees;
+ private readonly Func externalStorageProvider;
+ private readonly IOrganization organizations;
+ private readonly ISharepointSynchronizationSettings serviceSettings;
+ private readonly IEqualityComparer sharepointStorageItemComparer = new SharepointStorageItemComparer();
+ private readonly ISickLeaves sickLeaves;
+ private readonly IVacations vacations;
+ private readonly IWorkHoursCredit workouts;
- public Sharepoint(StatelessServiceContext context, ISharepointOnlineConfiguration sharepointConnectSettings, ISharepointDepartmentsCalendarsSettings departmentsSettings)
+ public Sharepoint(
+ StatelessServiceContext context,
+ IVacations vacations,
+ IWorkHoursCredit workouts,
+ ISickLeaves sickLeaves,
+ IEmployees employees,
+ IOrganization organizations,
+ Func externalStorageProvider,
+ ISharepointSynchronizationSettings serviceSettings,
+ ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings)
: base(context)
{
- this.sharepointConnectSettings = sharepointConnectSettings;
- this.departmentsSettings = departmentsSettings;
+ this.externalStorageProvider = externalStorageProvider;
+ this.vacations = vacations;
+ this.workouts = workouts;
+ this.sickLeaves = sickLeaves;
+ this.employees = employees;
+ this.organizations = organizations;
+ this.serviceSettings = serviceSettings;
+ this.departmentsCalendarsSettings = departmentsCalendarsSettings;
}
///
@@ -41,19 +76,232 @@ protected override IEnumerable CreateServiceInstanceLis
/// Canceled when Service Fabric needs to shut down this service instance.
protected override async Task RunAsync(CancellationToken cancellationToken)
{
- // TODO: Replace the following sample code with your own logic
- // or remove this RunAsync override if it's not needed in your service.
-
- long iterations = 0;
-
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
- ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
+ //ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
+
+ // request Sharepoint calendars
+ var externalStorage = this.externalStorageProvider();
+ var departments = await this.GetDepartmentsList(cancellationToken);
+ var sharepointCalendars = departments.Distinct().ToDictionary(x => x,
+ dId => this.GetSharepointCalendarsByDepartment(dId).ToDictionary(x => x,
+ async cal => await this.GetAllSharepointItemsForCalendar(externalStorage, cal)));
+
+ foreach (var departmentId in departments)
+ {
+ var departmentEmployes = await this.employees.FindEmployeesAsync(EmployeesQuery.Create().ForDepartment(departmentId), cancellationToken);
+ var employeeIds = departmentEmployes.Select(x => x.EmployeeId).ToArray();
+
+ var employeeVacations = await this.vacations.GetCalendarEventsByEmployeeAsync(employeeIds, cancellationToken);
+ var employeeVacationValues = employeeVacations.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
+
+ var employeeWorkouts = await this.workouts.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
+ var employeeWorkoutsValues = employeeWorkouts.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
+
+ var employeeSickLeaves = await this.sickLeaves.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
+ var employeeSickLeavesValues = employeeSickLeaves.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
+
+ foreach (var calendar in this.GetSharepointCalendarsByDepartment(departmentId))
+ {
+ var storageItems = await sharepointCalendars[departmentId][calendar];
+
+ var employeeVacationsStorageItems = storageItems.Where(x => x.Category == CalendarEventTypes.Vacation).ToList();
+ var employeeWorkoutsStorageItems = storageItems.Where(x => x.Category == CalendarEventTypes.Workout).ToList();
+ var employeeSickLeavesStorageItems = storageItems.Where(x => x.Category == CalendarEventTypes.Sickleave).ToList();
+
+ #region vacation synchronization
+
+ // synchronize vacations for selected department
+ try
+ {
+ var removeStorageItems = employeeVacationsStorageItems.Where(x => !employeeVacationValues.Keys.Contains(x.CalendarEventId)).ToArray();
+
+ // insert or update items
+ foreach (var vacationEventId in employeeVacationValues.Keys)
+ {
+ var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == employeeVacationValues[vacationEventId].EmployeeId);
+ await this.UpsertVacation(vacationEventId, calendar, employeeVacationValues[vacationEventId], employeeMetadata, externalStorage, cancellationToken);
+ }
+
+ // remove redundant items
+ foreach (var item in removeStorageItems)
+ {
+ await externalStorage.DeleteItem(
+ calendar,
+ item.Id);
+ }
+ }
+ catch (Exception e)
+ {
+ ServiceEventSource.Current.ServiceMessage(this.Context, e.ToString());
+ }
+
+ #endregion
+
+ #region workhours synchronization
+
+ // synchronize workouts for selected department
+ try
+ {
+ var removeStorageItems = employeeWorkoutsStorageItems.Where(x => !employeeWorkoutsValues.Keys.Contains(x.CalendarEventId)).ToArray();
+
+ // insert or update items
+ foreach (var workHourEventId in employeeWorkoutsValues.Keys)
+ {
+ var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == employeeWorkoutsValues[workHourEventId].EmployeeId);
+ await this.UpsertWorkHour(workHourEventId, calendar, employeeWorkoutsValues[workHourEventId], employeeMetadata, externalStorage, cancellationToken);
+ }
+
+ // remove redundant items
+ foreach (var item in removeStorageItems)
+ {
+ await externalStorage.DeleteItem(
+ calendar,
+ item.Id);
+ }
+ }
+ catch (Exception e)
+ {
+ ServiceEventSource.Current.ServiceMessage(this.Context, e.ToString());
+ }
+
+ #endregion
+
+ #region sick lives synchronization
+
+ // synchronize vacations for selected department
+ try
+ {
+ var removeStorageItems = employeeSickLeavesStorageItems.Where(x => !employeeSickLeavesValues.Keys.Contains(x.CalendarEventId)).ToArray();
+
+ // insert or update items
+ foreach (var sickLeaveEventId in employeeSickLeavesValues.Keys)
+ {
+ var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == employeeSickLeavesValues[sickLeaveEventId].EmployeeId);
+ await this.UpsertSickLeave(sickLeaveEventId, calendar, employeeSickLeavesValues[sickLeaveEventId], employeeMetadata, externalStorage, cancellationToken);
+ }
+
+ // remove redundant items
+ foreach (var item in removeStorageItems)
+ {
+ await externalStorage.DeleteItem(
+ calendar,
+ item.Id);
+ }
+ }
+ catch (Exception e)
+ {
+ ServiceEventSource.Current.ServiceMessage(this.Context, e.ToString());
+ }
+
+ #endregion
+ }
+ }
+
+#if DEBUG
+ await Task.Delay(TimeSpan.FromMinutes(15), cancellationToken);
+#else
+ await Task.Delay(TimeSpan.FromMinutes(serviceSettings.SynchronizationIntervalMinutes), cancellationToken);
+#endif
+ }
+ }
+
+ private async Task> GetDepartmentsList(CancellationToken cancellationToken)
+ {
+ if (this.departmentsCalendarsSettings.DepartmentsCalendars != null && this.departmentsCalendarsSettings.DepartmentsCalendars.Any())
+ {
+ return this.departmentsCalendarsSettings.DepartmentsCalendars.Select(x => x.DepartmentId);
+ }
+
+ return (await this.organizations.GetDepartmentsAsync(cancellationToken)).Select(x => x.DepartmentId.Value.ToString());
+ }
+
+ private IEnumerable GetSharepointCalendarsByDepartment(string departmentId)
+ {
+ return this.departmentsCalendarsSettings.DepartmentsCalendars
+ .Where(x => x.DepartmentId == departmentId)
+ .Select(x => x.Calendar);
+ }
+
+ private async Task> GetAllSharepointItemsForCalendar(IExternalStorage externalStorage, string calendar)
+ {
+ return await externalStorage.GetItems(calendar);
+ }
+
+ private async Task GetSharepointItemForCalendarEvent(IExternalStorage externalStorage, string calendar, string eventId)
+ {
+ var existingItems = await externalStorage.GetItems(
+ calendar,
+ new[] { new EqualCondition(x => x.CalendarEventId, eventId) });
+ return existingItems.SingleOrDefault();
+ }
- await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
+ private async Task UpsertVacation(string eventId, string calendar, VacationDescription vacation, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
+ {
+ var datesPeriod = new DatesPeriod(vacation.StartDate.Date, vacation.EndDate.Date);
+ var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Vacation, datesPeriod, employeeMetadata);
+ await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
+ }
+
+ private async Task UpsertWorkHour(string eventId, string calendar, WorkHoursChange workHours, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
+ {
+ var datesPeriod = new DatesPeriod(workHours.Date, workHours.Date);
+ var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Workout, datesPeriod, employeeMetadata);
+ await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
+ }
+
+ private async Task UpsertSickLeave(string eventId, string calendar, SickLeaveDescription sickLeave, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
+ {
+ var datesPeriod = new DatesPeriod(sickLeave.StartDate.Date, sickLeave.EndDate.Date);
+ var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Sickleave, datesPeriod, employeeMetadata);
+ await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
+ }
+
+ private async Task UpsertStorageItem(string eventId, string calendar, DatesPeriod datesPeriod, StorageItem upsertItem, IExternalStorage externalStorage, CancellationToken cancellationToken)
+ {
+ var storageItem = await this.GetSharepointItemForCalendarEvent(externalStorage, calendar, eventId);
+
+ if (storageItem == null)
+ {
+ await externalStorage.AddItem(
+ calendar,
+ upsertItem,
+ cancellationToken);
+ }
+ else if (!this.sharepointStorageItemComparer.Equals(upsertItem, storageItem))
+ {
+ upsertItem.Id = storageItem.Id;
+ await externalStorage.UpdateItem(
+ calendar,
+ upsertItem,
+ cancellationToken);
}
}
+
+ private StorageItem CalendarEventToStorageItem(string eventId, string calendarEventType, DatesPeriod period, EmployeeMetadata employeeMetadata)
+ {
+ var totalHours = period.FinishWorkingHour - period.StartWorkingHour;
+
+ var longEventsTitle = $"{employeeMetadata.Name} ({calendarEventType})";
+ var shortEventsTitle = $"{employeeMetadata.Name} ({calendarEventType}: {totalHours} hours)";
+
+ var title = calendarEventType == CalendarEventTypes.Vacation || calendarEventType == CalendarEventTypes.Sickleave
+ ? longEventsTitle
+ : shortEventsTitle;
+
+ var storageItem = new StorageItem
+ {
+ Title = title,
+ StartDate = period.StartDate,
+ EndDate = period.EndDate,
+ Category = calendarEventType,
+ AllDayEvent = true,
+ CalendarEventId = eventId
+ };
+
+ return storageItem;
+ }
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
index 8c2450b3e..f9e194889 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
@@ -1,12 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Text.Json;
-
-namespace Arcadia.Assistant.Sharepoint
+namespace Arcadia.Assistant.Sharepoint
{
+ using System;
+ using System.Collections.Generic;
using System.Fabric.Description;
- using System.Text.Json.Serialization;
+ using System.Text.Json;
public class SharepointDepartmentsCalendarsSettings : ISharepointDepartmentsCalendarsSettings
{
@@ -26,4 +23,4 @@ public SharepointDepartmentsCalendarsSettings(ConfigurationSection configuration
public IEnumerable DepartmentsCalendars { get; set; }
}
-}
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointStorageItemComparer.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointStorageItemComparer.cs
new file mode 100644
index 000000000..162a3ca2d
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointStorageItemComparer.cs
@@ -0,0 +1,58 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System.Collections.Generic;
+
+ using ExternalStorages.Abstractions;
+
+ public sealed class SharepointStorageItemComparer : IEqualityComparer
+ {
+ public bool Equals(StorageItem x, StorageItem y)
+ {
+ if (ReferenceEquals(x, y))
+ {
+ return true;
+ }
+
+ if (ReferenceEquals(x, null))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(y, null))
+ {
+ return false;
+ }
+
+ if (x.GetType() != y.GetType())
+ {
+ return false;
+ }
+
+ return
+ string.Equals(x.Id, y.Id) &&
+ string.Equals(x.Title, y.Title) &&
+ string.Equals(x.Description, y.Description) &&
+ x.StartDate.Date.Equals(y.StartDate.Date) &&
+ x.EndDate.Date.Equals(y.EndDate.Date) &&
+ string.Equals(x.Category, y.Category) &&
+ x.AllDayEvent == y.AllDayEvent &&
+ string.Equals(x.CalendarEventId, y.CalendarEventId);
+ }
+
+ public int GetHashCode(StorageItem obj)
+ {
+ unchecked
+ {
+ var hashCode = obj.Id.GetHashCode();
+ hashCode = (hashCode * 397) ^ obj.Title.GetHashCode();
+ hashCode = (hashCode * 397) ^ obj.Description.GetHashCode();
+ hashCode = (hashCode * 397) ^ obj.StartDate.Date.GetHashCode();
+ hashCode = (hashCode * 397) ^ obj.EndDate.Date.GetHashCode();
+ hashCode = (hashCode * 397) ^ obj.Category.GetHashCode();
+ hashCode = (hashCode * 397) ^ obj.AllDayEvent.GetHashCode();
+ hashCode = (hashCode * 397) ^ obj.CalendarEventId.GetHashCode();
+ return hashCode;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizationSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizationSettings.cs
new file mode 100644
index 000000000..8711dad4f
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizationSettings.cs
@@ -0,0 +1,16 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System.Fabric.Description;
+
+ public class SharepointSynchronizationSettings : ISharepointSynchronizationSettings
+ {
+ private readonly int DefaultSynchronizationIntervalMinutes = 240;
+
+ public SharepointSynchronizationSettings(ConfigurationSection configurationSection)
+ {
+ this.SynchronizationIntervalMinutes = int.TryParse(configurationSection.Parameters["SynchronizationIntervalMinutes"].Value, out var interval) ? interval : this.DefaultSynchronizationIntervalMinutes;
+ }
+
+ public int SynchronizationIntervalMinutes { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs
new file mode 100644
index 000000000..380591bcc
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs
@@ -0,0 +1,19 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using Calendar.Abstractions;
+
+ using Employees.Contracts;
+
+ public class StoreCalendarEventToSharepoint
+ {
+ public StoreCalendarEventToSharepoint(CalendarEvent @event, EmployeeMetadata employeeMetadata)
+ {
+ this.Event = @event;
+ this.EmployeeMetadata = employeeMetadata;
+ }
+
+ public CalendarEvent Event { get; }
+
+ public EmployeeMetadata EmployeeMetadata { get; }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs
index 0aeba094f..915b1f1bf 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs
@@ -6,6 +6,7 @@
namespace Arcadia.Assistant.SickLeaves.Contracts
{
using System;
+ using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -17,6 +18,8 @@ public interface ISickLeaves : IService
{
Task GetCalendarEventsAsync(EmployeeId employeeId, CancellationToken cancellationToken);
+ Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken);
+
Task GetCalendarEventAsync(EmployeeId employeeId, int eventId, CancellationToken cancellationToken);
Task CreateSickLeaveAsync(EmployeeId employeeId, DateTime startDate, DateTime endDate);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/SickLeaveDescription.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/SickLeaveDescription.cs
index 163cf3612..2e8dacd65 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/SickLeaveDescription.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/SickLeaveDescription.cs
@@ -3,12 +3,17 @@
using System;
using System.Runtime.Serialization;
+ using Employees.Contracts;
+
[DataContract]
public class SickLeaveDescription
{
[DataMember]
public int SickLeaveId { get; set; }
+ [DataMember]
+ public EmployeeId EmployeeId { get; set; }
+
[DataMember]
public DateTime StartDate { get; set; }
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaveModelConverter.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaveModelConverter.cs
index b41eee54a..ea16324f9 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaveModelConverter.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaveModelConverter.cs
@@ -6,12 +6,15 @@
using Contracts;
+ using Employees.Contracts;
+
public class SickLeaveModelConverter
{
public Expression> ToDescription { get; }
= model => new SickLeaveDescription()
{
SickLeaveId = model.Id,
+ EmployeeId = new EmployeeId(model.EmployeeId),
StartDate = model.Start,
EndDate = model.End,
Status = model.SickLeaveCancellations.Any() ? SickLeaveStatus.Cancelled
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs
index 7653cd969..baa621881 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs
@@ -80,6 +80,18 @@ public async Task GetCalendarEventsAsync(EmployeeId empl
return sickLeaves;
}
+ public async Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken)
+ {
+ using var db = this.dbFactory();
+ var sickLeaves = await db.Value
+ .SickLeaves
+ .Where(x => employeeIds.Any(id => x.EmployeeId == id.Value))
+ .Select(this.modelConverter.ToDescription)
+ .ToArrayAsync(cancellationToken);
+
+ return sickLeaves.GroupBy(x => x.EmployeeId).ToDictionary(x => x.Key, x => x.ToArray());
+ }
+
public async Task GetCalendarEventAsync(EmployeeId employeeId, int eventId, CancellationToken cancellationToken)
{
using var db = this.dbFactory();
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs
index 5768e3f43..4d58f9fc3 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs
@@ -24,6 +24,8 @@ public interface IWorkHoursCredit : IService
Task GetCalendarEventsAsync(EmployeeId employeeId, CancellationToken cancellationToken);
+ Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken);
+
Task GetCalendarEventAsync(EmployeeId employeeId, Guid eventId, CancellationToken cancellationToken);
Task> GetActiveRequestsAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
index df16d5e64..59e5056a0 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
@@ -77,6 +77,15 @@ public async Task GetCalendarEventsAsync(EmployeeId employeeI
return events;
}
+ public async Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken)
+ {
+ using var ctx = this.dbFactory();
+ var events = await this.QueryCalendarEvents(ctx.Value.ChangeRequests, x => employeeIds.Any(id => id.Value == x.EmployeeId))
+ .ToArrayAsync(cancellationToken);
+
+ return events.GroupBy(x=> x.EmployeeId).ToDictionary(x => x.Key, x=> x.ToArray());
+ }
+
public async Task GetCalendarEventAsync(EmployeeId employeeId, Guid eventId, CancellationToken cancellationToken)
{
using var ctx = this.dbFactory();
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.sln b/server2/Arcadia.Assistant/Arcadia.Assistant.sln
index ba512d951..f01d7caf9 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.sln
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.sln
@@ -75,6 +75,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.ExternalS
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.Sharepoint", "Arcadia.Assistant.Sharepoint\Arcadia.Assistant.Sharepoint.csproj", "{C5818540-DECF-409D-82CE-D34E72C34D7D}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.Calendar.Abstractions", "Arcadia.Assistant.Calendar.Abstractions\Arcadia.Assistant.Calendar.Abstractions.csproj", "{2D9BD0AE-9420-4743-96EF-C6F39F46C39F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -339,6 +341,14 @@ Global
{C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|Any CPU.Build.0 = Release|Any CPU
{C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|x64.ActiveCfg = Release|Any CPU
{C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|x64.Build.0 = Release|Any CPU
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|x64.ActiveCfg = Debug|x64
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|x64.Build.0 = Debug|x64
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|x64.ActiveCfg = Release|x64
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -364,6 +374,7 @@ Global
{9507586E-6D63-432E-99E2-59B73907ACA0} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
{085366A3-3D08-45B5-9A24-DDF3E1EEB2FA} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
{C5818540-DECF-409D-82CE-D34E72C34D7D} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
+ {2D9BD0AE-9420-4743-96EF-C6F39F46C39F} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C376673D-F593-42B1-8093-D1F667D586B1}
From efc96b8da86ef9655ab41514938b21c5c26e8492 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Wed, 25 Dec 2019 13:20:00 +0300
Subject: [PATCH 04/14] Fix PR comments
---
.../EmployeeMetadata.cs | 2 +-
.../SharepointStorageItemComparer.cs | 4 +-
.../PublishProfiles/Azure.xml | 2 +-
.../Arcadia.Assistant.Sharepoint.csproj | 1 -
.../CalendarEvent/CalendarEventTypes.cs | 11 +
.../CalendarEvent/CspCalendarEventIdParser.cs | 29 +++
.../CalendarEvent/DatesPeriod.cs | 104 +++++++++
...ISharepointDepartmentsCalendarsSettings.cs | 2 +
.../SharepointDepartmentCalendarMapping.cs | 2 +-
.../SharepointDepartmentsCalendarsSettings.cs | 2 +-
.../SharepointSynchronizationSettings.cs | 2 +-
.../Arcadia.Assistant.Sharepoint/Program.cs | 2 +
.../RemoveCalendarEventFromSharepoint.cs | 19 --
.../ServiceEventSource.cs | 2 +-
.../Sharepoint.cs | 215 +-----------------
.../SharepointItemSynchronization.cs | 143 ++++++++++++
.../SharepointSynchronizator.cs | 171 ++++++++++++++
.../StoreCalendarEventToSharepoint.cs | 19 --
.../Arcadia.Assistant/Arcadia.Assistant.sln | 11 -
19 files changed, 479 insertions(+), 264 deletions(-)
rename server2/Arcadia.Assistant/{Arcadia.Assistant.Sharepoint => Arcadia.Assistant.ExternalStorages.Abstractions}/SharepointStorageItemComparer.cs (95%)
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CalendarEventTypes.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CspCalendarEventIdParser.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/DatesPeriod.cs
rename server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/{ => Models}/SharepointDepartmentCalendarMapping.cs (86%)
rename server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/{ => Models}/SharepointDepartmentsCalendarsSettings.cs (94%)
rename server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/{ => Models}/SharepointSynchronizationSettings.cs (92%)
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs
index d1facefa8..04aaac07e 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees.Contracts/EmployeeMetadata.cs
@@ -59,7 +59,7 @@ public EmployeeMetadata(EmployeeId employeeId, string email)
return CalculateYearsFromDate(this.BirthDate, date);
}
- public string Name => $"{this.FirstName ?? string.Empty} {this.LastName ?? string.Empty}";
+ public string Name => $"{this.FirstName ?? string.Empty} {this.LastName ?? string.Empty}".Trim();
public int? YearsServedAt(DateTime date)
{
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointStorageItemComparer.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/SharepointStorageItemComparer.cs
similarity index 95%
rename from server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointStorageItemComparer.cs
rename to server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/SharepointStorageItemComparer.cs
index 162a3ca2d..2710c3aa0 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointStorageItemComparer.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/SharepointStorageItemComparer.cs
@@ -1,9 +1,7 @@
-namespace Arcadia.Assistant.Sharepoint
+namespace Arcadia.Assistant.ExternalStorages.Abstractions
{
using System.Collections.Generic;
- using ExternalStorages.Abstractions;
-
public sealed class SharepointStorageItemComparer : IEqualityComparer
{
public bool Equals(StorageItem x, StorageItem y)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/PublishProfiles/Azure.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/PublishProfiles/Azure.xml
index d57ba57bd..c2e63a7fb 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/PublishProfiles/Azure.xml
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/PublishProfiles/Azure.xml
@@ -20,7 +20,7 @@
AzureActiveDirectory="true"
ServerCertThumbprint="0123456789012345678901234567890123456789" />
-->
-
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
index 321eebb44..986c111a4 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
@@ -19,7 +19,6 @@
-
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CalendarEventTypes.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CalendarEventTypes.cs
new file mode 100644
index 000000000..f1928af76
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CalendarEventTypes.cs
@@ -0,0 +1,11 @@
+namespace Arcadia.Assistant.Sharepoint.CalendarEvent
+{
+ public static class CalendarEventTypes
+ {
+ public const string Vacation = "Vacation";
+
+ public const string Workout = "Workout";
+
+ public const string Sickleave = "Sickleave";
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CspCalendarEventIdParser.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CspCalendarEventIdParser.cs
new file mode 100644
index 000000000..72027daaa
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/CspCalendarEventIdParser.cs
@@ -0,0 +1,29 @@
+namespace Arcadia.Assistant.Sharepoint.CalendarEvent
+{
+ using System;
+
+ public static class CspCalendarEventIdParser
+ {
+ public static int GetCspIdFromCalendarEvent(string calendarEventId, string calendarEventType)
+ {
+ var parts = calendarEventId.Split('_');
+
+ if (parts.Length != 2 || parts[0] != calendarEventType || !int.TryParse(parts[1], out var cspId))
+ {
+ throw new ArgumentException("Calendar event id has wrong format");
+ }
+
+ return cspId;
+ }
+
+ public static string GetCalendarEventIdFromCspId(int cspId, string calendarEventType)
+ {
+ return $"{calendarEventType}_{cspId}";
+ }
+
+ public static string GetCalendarEventIdFromCspId(Guid cspId, string calendarEventType)
+ {
+ return $"{calendarEventType}_{cspId}";
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/DatesPeriod.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/DatesPeriod.cs
new file mode 100644
index 000000000..a1a2bfd59
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/CalendarEvent/DatesPeriod.cs
@@ -0,0 +1,104 @@
+namespace Arcadia.Assistant.Sharepoint.CalendarEvent
+{
+ using System;
+
+ public sealed class DatesPeriod
+ {
+ public DatesPeriod(DateTime startDate, DateTime endDate, int startWorkingHour = 0, int finishWorkingHour = 8)
+ {
+ this.StartDate = MinDate(startDate, endDate);
+ this.EndDate = MaxDate(startDate, endDate);
+ this.StartWorkingHour = Math.Min(startWorkingHour, finishWorkingHour);
+ this.FinishWorkingHour = Math.Max(startWorkingHour, finishWorkingHour);
+ }
+
+ public DateTime StartDate { get; }
+
+ public DateTime EndDate { get; }
+
+ ///
+ /// Starting working hour index. Typically, 0 or 4.
+ ///
+ public int StartWorkingHour { get; }
+
+ ///
+ /// Finish working hour index. Typically, 4 or 8
+ ///
+ public int FinishWorkingHour { get; }
+
+ private bool Equals(DatesPeriod other)
+ {
+ return this.StartDate.Equals(other.StartDate)
+ && this.EndDate.Equals(other.EndDate)
+ && this.StartWorkingHour == other.StartWorkingHour
+ && this.FinishWorkingHour == other.FinishWorkingHour;
+ }
+
+ public override bool Equals(object? obj)
+ {
+ if (ReferenceEquals(null, obj))
+ {
+ return false;
+ }
+
+ if (ReferenceEquals(this, obj))
+ {
+ return true;
+ }
+
+ if (obj.GetType() != this.GetType())
+ {
+ return false;
+ }
+
+ return this.Equals((DatesPeriod)obj);
+ }
+
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ var hashCode = this.StartDate.GetHashCode();
+ hashCode = (hashCode * 397) ^ this.EndDate.GetHashCode();
+ hashCode = (hashCode * 397) ^ this.StartWorkingHour;
+ hashCode = (hashCode * 397) ^ this.FinishWorkingHour;
+ return hashCode;
+ }
+ }
+
+ public static bool operator ==(DatesPeriod left, DatesPeriod right)
+ {
+ return Equals(left, right);
+ }
+
+ public static bool operator !=(DatesPeriod left, DatesPeriod right)
+ {
+ return !Equals(left, right);
+ }
+
+ public bool DatesIntersectsWith(DatesPeriod? period)
+ {
+ if (period == null)
+ {
+ return false;
+ }
+
+ if (this.EndDate < period.StartDate || period.EndDate < this.StartDate)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ private static DateTime MinDate(DateTime first, DateTime second)
+ {
+ return first <= second ? first : second;
+ }
+
+ private static DateTime MaxDate(DateTime first, DateTime second)
+ {
+ return first >= second ? first : second;
+ }
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs
index acf7a057a..1b4df8aae 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ISharepointDepartmentsCalendarsSettings.cs
@@ -2,6 +2,8 @@
{
using System.Collections.Generic;
+ using Models;
+
public interface ISharepointDepartmentsCalendarsSettings
{
IEnumerable DepartmentsCalendars { get; }
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentCalendarMapping.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentCalendarMapping.cs
similarity index 86%
rename from server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentCalendarMapping.cs
rename to server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentCalendarMapping.cs
index ffb1e8289..8e297d81f 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentCalendarMapping.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentCalendarMapping.cs
@@ -1,4 +1,4 @@
-namespace Arcadia.Assistant.Sharepoint
+namespace Arcadia.Assistant.Sharepoint.Models
{
using System.Text.Json.Serialization;
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentsCalendarsSettings.cs
similarity index 94%
rename from server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
rename to server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentsCalendarsSettings.cs
index f9e194889..ee53f0169 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointDepartmentsCalendarsSettings.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentsCalendarsSettings.cs
@@ -1,4 +1,4 @@
-namespace Arcadia.Assistant.Sharepoint
+namespace Arcadia.Assistant.Sharepoint.Models
{
using System;
using System.Collections.Generic;
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizationSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointSynchronizationSettings.cs
similarity index 92%
rename from server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizationSettings.cs
rename to server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointSynchronizationSettings.cs
index 8711dad4f..f3f022a3a 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizationSettings.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointSynchronizationSettings.cs
@@ -1,4 +1,4 @@
-namespace Arcadia.Assistant.Sharepoint
+namespace Arcadia.Assistant.Sharepoint.Models
{
using System.Fabric.Description;
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
index f386300e1..cb090842b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
@@ -19,6 +19,8 @@ namespace Arcadia.Assistant.Sharepoint
using Microsoft.Extensions.DependencyInjection;
using Microsoft.ServiceFabric.Services.Remoting.Client;
+ using Models;
+
using Organization.Contracts;
using SickLeaves.Contracts;
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs
deleted file mode 100644
index dbea220c9..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/RemoveCalendarEventFromSharepoint.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Arcadia.Assistant.Sharepoint
-{
- using Calendar.Abstractions;
-
- using Employees.Contracts;
-
- public class RemoveCalendarEventFromSharepoint
- {
- public RemoveCalendarEventFromSharepoint(CalendarEvent @event, EmployeeMetadata employeeMetadata)
- {
- this.Event = @event;
- this.EmployeeMetadata = employeeMetadata;
- }
-
- public CalendarEvent Event { get; }
-
- public EmployeeMetadata EmployeeMetadata { get; }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs
index 642073ea7..bd30ab49d 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/ServiceEventSource.cs
@@ -4,7 +4,7 @@ namespace Arcadia.Assistant.Sharepoint
using System.Diagnostics.Tracing;
using System.Fabric;
- [EventSource(Name = "MyCompany-Arcadia.Assistant.SF-Arcadia.Assistant.Sharepoint")]
+ [EventSource(Name = "Arcadia.Assistant.SF-Arcadia.Assistant.Sharepoint")]
internal sealed class ServiceEventSource : EventSource
{
public static readonly ServiceEventSource Current = new ServiceEventSource();
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
index 8aa1b0a25..55ca6e628 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
@@ -7,12 +7,11 @@ namespace Arcadia.Assistant.Sharepoint
using System.Threading;
using System.Threading.Tasks;
- using Calendar.Abstractions;
-
using Employees.Contracts;
using ExternalStorages.Abstractions;
+ using Microsoft.Extensions.Logging;
using Microsoft.ServiceFabric.Services.Communication.Runtime;
using Microsoft.ServiceFabric.Services.Runtime;
@@ -32,9 +31,9 @@ public class Sharepoint : StatelessService
private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
private readonly IEmployees employees;
private readonly Func externalStorageProvider;
+ private readonly ILogger? logger = null; // TO DO - initialize variable
private readonly IOrganization organizations;
private readonly ISharepointSynchronizationSettings serviceSettings;
- private readonly IEqualityComparer sharepointStorageItemComparer = new SharepointStorageItemComparer();
private readonly ISickLeaves sickLeaves;
private readonly IVacations vacations;
private readonly IWorkHoursCredit workouts;
@@ -85,126 +84,18 @@ protected override async Task RunAsync(CancellationToken cancellationToken)
// request Sharepoint calendars
var externalStorage = this.externalStorageProvider();
var departments = await this.GetDepartmentsList(cancellationToken);
- var sharepointCalendars = departments.Distinct().ToDictionary(x => x,
- dId => this.GetSharepointCalendarsByDepartment(dId).ToDictionary(x => x,
- async cal => await this.GetAllSharepointItemsForCalendar(externalStorage, cal)));
+ var synchronizator = new SharepointSynchronizator(this.sickLeaves, this.vacations, this.workouts, this.departmentsCalendarsSettings, this.logger);
- foreach (var departmentId in departments)
+ try
{
- var departmentEmployes = await this.employees.FindEmployeesAsync(EmployeesQuery.Create().ForDepartment(departmentId), cancellationToken);
- var employeeIds = departmentEmployes.Select(x => x.EmployeeId).ToArray();
-
- var employeeVacations = await this.vacations.GetCalendarEventsByEmployeeAsync(employeeIds, cancellationToken);
- var employeeVacationValues = employeeVacations.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
-
- var employeeWorkouts = await this.workouts.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
- var employeeWorkoutsValues = employeeWorkouts.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
-
- var employeeSickLeaves = await this.sickLeaves.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
- var employeeSickLeavesValues = employeeSickLeaves.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
-
- foreach (var calendar in this.GetSharepointCalendarsByDepartment(departmentId))
- {
- var storageItems = await sharepointCalendars[departmentId][calendar];
-
- var employeeVacationsStorageItems = storageItems.Where(x => x.Category == CalendarEventTypes.Vacation).ToList();
- var employeeWorkoutsStorageItems = storageItems.Where(x => x.Category == CalendarEventTypes.Workout).ToList();
- var employeeSickLeavesStorageItems = storageItems.Where(x => x.Category == CalendarEventTypes.Sickleave).ToList();
-
- #region vacation synchronization
-
- // synchronize vacations for selected department
- try
- {
- var removeStorageItems = employeeVacationsStorageItems.Where(x => !employeeVacationValues.Keys.Contains(x.CalendarEventId)).ToArray();
-
- // insert or update items
- foreach (var vacationEventId in employeeVacationValues.Keys)
- {
- var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == employeeVacationValues[vacationEventId].EmployeeId);
- await this.UpsertVacation(vacationEventId, calendar, employeeVacationValues[vacationEventId], employeeMetadata, externalStorage, cancellationToken);
- }
-
- // remove redundant items
- foreach (var item in removeStorageItems)
- {
- await externalStorage.DeleteItem(
- calendar,
- item.Id);
- }
- }
- catch (Exception e)
- {
- ServiceEventSource.Current.ServiceMessage(this.Context, e.ToString());
- }
-
- #endregion
-
- #region workhours synchronization
-
- // synchronize workouts for selected department
- try
- {
- var removeStorageItems = employeeWorkoutsStorageItems.Where(x => !employeeWorkoutsValues.Keys.Contains(x.CalendarEventId)).ToArray();
-
- // insert or update items
- foreach (var workHourEventId in employeeWorkoutsValues.Keys)
- {
- var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == employeeWorkoutsValues[workHourEventId].EmployeeId);
- await this.UpsertWorkHour(workHourEventId, calendar, employeeWorkoutsValues[workHourEventId], employeeMetadata, externalStorage, cancellationToken);
- }
-
- // remove redundant items
- foreach (var item in removeStorageItems)
- {
- await externalStorage.DeleteItem(
- calendar,
- item.Id);
- }
- }
- catch (Exception e)
- {
- ServiceEventSource.Current.ServiceMessage(this.Context, e.ToString());
- }
-
- #endregion
-
- #region sick lives synchronization
-
- // synchronize vacations for selected department
- try
- {
- var removeStorageItems = employeeSickLeavesStorageItems.Where(x => !employeeSickLeavesValues.Keys.Contains(x.CalendarEventId)).ToArray();
-
- // insert or update items
- foreach (var sickLeaveEventId in employeeSickLeavesValues.Keys)
- {
- var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == employeeSickLeavesValues[sickLeaveEventId].EmployeeId);
- await this.UpsertSickLeave(sickLeaveEventId, calendar, employeeSickLeavesValues[sickLeaveEventId], employeeMetadata, externalStorage, cancellationToken);
- }
-
- // remove redundant items
- foreach (var item in removeStorageItems)
- {
- await externalStorage.DeleteItem(
- calendar,
- item.Id);
- }
- }
- catch (Exception e)
- {
- ServiceEventSource.Current.ServiceMessage(this.Context, e.ToString());
- }
-
- #endregion
- }
+ await synchronizator.Synchronize(this.employees, departments, externalStorage, cancellationToken);
+ }
+ catch (Exception e)
+ {
+ this.logger?.LogError(e, "Sharepoint items synchronization fail");
}
-#if DEBUG
- await Task.Delay(TimeSpan.FromMinutes(15), cancellationToken);
-#else
- await Task.Delay(TimeSpan.FromMinutes(serviceSettings.SynchronizationIntervalMinutes), cancellationToken);
-#endif
+ await Task.Delay(TimeSpan.FromMinutes(this.serviceSettings.SynchronizationIntervalMinutes), cancellationToken);
}
}
@@ -217,91 +108,5 @@ private async Task> GetDepartmentsList(CancellationToken can
return (await this.organizations.GetDepartmentsAsync(cancellationToken)).Select(x => x.DepartmentId.Value.ToString());
}
-
- private IEnumerable GetSharepointCalendarsByDepartment(string departmentId)
- {
- return this.departmentsCalendarsSettings.DepartmentsCalendars
- .Where(x => x.DepartmentId == departmentId)
- .Select(x => x.Calendar);
- }
-
- private async Task> GetAllSharepointItemsForCalendar(IExternalStorage externalStorage, string calendar)
- {
- return await externalStorage.GetItems(calendar);
- }
-
- private async Task GetSharepointItemForCalendarEvent(IExternalStorage externalStorage, string calendar, string eventId)
- {
- var existingItems = await externalStorage.GetItems(
- calendar,
- new[] { new EqualCondition(x => x.CalendarEventId, eventId) });
- return existingItems.SingleOrDefault();
- }
-
- private async Task UpsertVacation(string eventId, string calendar, VacationDescription vacation, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
- {
- var datesPeriod = new DatesPeriod(vacation.StartDate.Date, vacation.EndDate.Date);
- var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Vacation, datesPeriod, employeeMetadata);
- await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
- }
-
- private async Task UpsertWorkHour(string eventId, string calendar, WorkHoursChange workHours, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
- {
- var datesPeriod = new DatesPeriod(workHours.Date, workHours.Date);
- var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Workout, datesPeriod, employeeMetadata);
- await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
- }
-
- private async Task UpsertSickLeave(string eventId, string calendar, SickLeaveDescription sickLeave, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
- {
- var datesPeriod = new DatesPeriod(sickLeave.StartDate.Date, sickLeave.EndDate.Date);
- var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Sickleave, datesPeriod, employeeMetadata);
- await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
- }
-
- private async Task UpsertStorageItem(string eventId, string calendar, DatesPeriod datesPeriod, StorageItem upsertItem, IExternalStorage externalStorage, CancellationToken cancellationToken)
- {
- var storageItem = await this.GetSharepointItemForCalendarEvent(externalStorage, calendar, eventId);
-
- if (storageItem == null)
- {
- await externalStorage.AddItem(
- calendar,
- upsertItem,
- cancellationToken);
- }
- else if (!this.sharepointStorageItemComparer.Equals(upsertItem, storageItem))
- {
- upsertItem.Id = storageItem.Id;
- await externalStorage.UpdateItem(
- calendar,
- upsertItem,
- cancellationToken);
- }
- }
-
- private StorageItem CalendarEventToStorageItem(string eventId, string calendarEventType, DatesPeriod period, EmployeeMetadata employeeMetadata)
- {
- var totalHours = period.FinishWorkingHour - period.StartWorkingHour;
-
- var longEventsTitle = $"{employeeMetadata.Name} ({calendarEventType})";
- var shortEventsTitle = $"{employeeMetadata.Name} ({calendarEventType}: {totalHours} hours)";
-
- var title = calendarEventType == CalendarEventTypes.Vacation || calendarEventType == CalendarEventTypes.Sickleave
- ? longEventsTitle
- : shortEventsTitle;
-
- var storageItem = new StorageItem
- {
- Title = title,
- StartDate = period.StartDate,
- EndDate = period.EndDate,
- Category = calendarEventType,
- AllDayEvent = true,
- CalendarEventId = eventId
- };
-
- return storageItem;
- }
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
new file mode 100644
index 000000000..26c880b5b
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
@@ -0,0 +1,143 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ using CalendarEvent;
+
+ using Employees.Contracts;
+
+ using ExternalStorages.Abstractions;
+
+ using Microsoft.Extensions.Logging;
+
+ public abstract class SharepointItemSynchronization
+ {
+ #region ctor
+
+ protected SharepointItemSynchronization(IExternalStorage externalStorage, ILogger? logger = null)
+ {
+ this.ExternalStorage = externalStorage;
+ this.Logger = logger;
+ }
+
+ #endregion
+
+ #region public interface
+
+ public async Task SynchronizeItem(string calendar, EmployeeMetadata[] departmentEmployes, Dictionary values, IEnumerable storageItemsList, CancellationToken cancellationToken)
+ {
+ try
+ {
+ var storageItems = storageItemsList.Where(x => x.Category == this.ItemEventType).ToList();
+ var removeStorageItems = storageItems.Where(x => !values.Keys.Contains(x.CalendarEventId)).ToArray();
+
+ // insert or update items
+ foreach (var workHourEventId in values.Keys)
+ {
+ var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == this.GetItemEmployeeId(values[workHourEventId]));
+ await this.UpsertItem(workHourEventId, calendar, values[workHourEventId], employeeMetadata, this.ExternalStorage, cancellationToken);
+ }
+
+ // remove redundant items
+ foreach (var item in removeStorageItems)
+ {
+ await this.ExternalStorage.DeleteItem(
+ calendar,
+ item.Id);
+ }
+ }
+ catch (Exception e)
+ {
+ this.Logger?.LogError(e, $"'{typeof(T)}' synchronization error");
+ }
+ }
+
+ #endregion
+
+ #region variables
+
+ private readonly IEqualityComparer sharepointStorageItemComparer = new SharepointStorageItemComparer();
+ protected readonly IExternalStorage ExternalStorage;
+ protected readonly ILogger? Logger;
+
+ #endregion
+
+ #region virtual interface
+
+ protected abstract string ItemEventType { get; }
+
+ protected abstract DatesPeriod GetItemDatePeriod(T item);
+
+ protected abstract EmployeeId GetItemEmployeeId(T item);
+
+ #endregion
+
+ #region private
+
+ private async Task UpsertItem(string eventId, string calendar, T item, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
+ {
+ var datesPeriod = this.GetItemDatePeriod(item);
+ var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Sickleave, datesPeriod, employeeMetadata);
+ await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
+ }
+
+ private async Task GetSharepointItemForCalendarEvent(IExternalStorage externalStorage, string calendar, string eventId)
+ {
+ var existingItems = await externalStorage.GetItems(
+ calendar,
+ new[] { new EqualCondition(x => x.CalendarEventId, eventId) });
+ return existingItems.SingleOrDefault();
+ }
+
+ private async Task UpsertStorageItem(string eventId, string calendar, DatesPeriod datesPeriod, StorageItem upsertItem, IExternalStorage externalStorage, CancellationToken cancellationToken)
+ {
+ var storageItem = await this.GetSharepointItemForCalendarEvent(externalStorage, calendar, eventId);
+
+ if (storageItem == null)
+ {
+ await externalStorage.AddItem(
+ calendar,
+ upsertItem,
+ cancellationToken);
+ }
+ else if (!this.sharepointStorageItemComparer.Equals(upsertItem, storageItem))
+ {
+ upsertItem.Id = storageItem.Id;
+ await externalStorage.UpdateItem(
+ calendar,
+ upsertItem,
+ cancellationToken);
+ }
+ }
+
+ private StorageItem CalendarEventToStorageItem(string eventId, string calendarEventType, DatesPeriod period, EmployeeMetadata employeeMetadata)
+ {
+ var totalHours = period.FinishWorkingHour - period.StartWorkingHour;
+
+ var longEventsTitle = $"{employeeMetadata.Name} ({calendarEventType})";
+ var shortEventsTitle = $"{employeeMetadata.Name} ({calendarEventType}: {totalHours} hours)";
+
+ var title = calendarEventType == CalendarEventTypes.Vacation || calendarEventType == CalendarEventTypes.Sickleave
+ ? longEventsTitle
+ : shortEventsTitle;
+
+ var storageItem = new StorageItem
+ {
+ Title = title,
+ StartDate = period.StartDate,
+ EndDate = period.EndDate,
+ Category = calendarEventType,
+ AllDayEvent = true,
+ CalendarEventId = eventId
+ };
+
+ return storageItem;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
new file mode 100644
index 000000000..2c28ee474
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
@@ -0,0 +1,171 @@
+namespace Arcadia.Assistant.Sharepoint
+{
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ using CalendarEvent;
+
+ using Employees.Contracts;
+
+ using ExternalStorages.Abstractions;
+
+ using Microsoft.Extensions.Logging;
+
+ using SickLeaves.Contracts;
+
+ using Vacations.Contracts;
+
+ using WorkHoursCredit.Contracts;
+
+ public sealed class SharepointSynchronizator
+ {
+ #region ctor
+
+ public SharepointSynchronizator(ISickLeaves sickLeaves, IVacations vacations, IWorkHoursCredit workouts, ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings, ILogger? logger)
+ {
+ this.sickLeaves = sickLeaves;
+ this.vacations = vacations;
+ this.workouts = workouts;
+ this.departmentsCalendarsSettings = departmentsCalendarsSettings;
+ this.logger = logger;
+ }
+
+ #endregion
+
+ #region publci interface
+
+ public async Task Synchronize(IEmployees employees, IEnumerable departments, IExternalStorage storage, CancellationToken cancellationToken)
+ {
+ var vacationsSync = new EmployeeVacationsSynchronization(storage, this.logger);
+ var workoutsSync = new EmployeeWorkoutsSynchronization(storage, this.logger);
+ var sickLeavesSync = new EmployeeSickLeavesSynchronization(storage, this.logger);
+
+ var sharepointCalendars = departments.Distinct().ToDictionary(x => x,
+ dId => this.GetSharepointCalendarsByDepartment(dId).ToDictionary(x => x,
+ async cal => await this.GetAllSharepointItemsForCalendar(storage, cal)));
+
+ foreach (var departmentId in departments)
+ {
+ var departmentEmployes = await employees.FindEmployeesAsync(EmployeesQuery.Create().ForDepartment(departmentId), cancellationToken);
+ var employeeIds = departmentEmployes.Select(x => x.EmployeeId).ToArray();
+
+ var employeeVacations = await this.vacations.GetCalendarEventsByEmployeeAsync(employeeIds, cancellationToken);
+ var employeeVacationValues = employeeVacations.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
+
+ var employeeWorkouts = await this.workouts.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
+ var employeeWorkoutsValues = employeeWorkouts.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
+
+ var employeeSickLeaves = await this.sickLeaves.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
+ var employeeSickLeavesValues = employeeSickLeaves.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
+
+ foreach (var calendar in this.GetSharepointCalendarsByDepartment(departmentId))
+ {
+ var storageItems = await sharepointCalendars[departmentId][calendar];
+
+ // synchronize vacations for selected department
+ await vacationsSync.SynchronizeItem(calendar, departmentEmployes, employeeVacationValues, storageItems, cancellationToken);
+
+ // synchronize workouts for selected department
+ await workoutsSync.SynchronizeItem(calendar, departmentEmployes, employeeWorkoutsValues, storageItems, cancellationToken);
+
+ // synchronize vacations for selected department
+ await sickLeavesSync.SynchronizeItem(calendar, departmentEmployes, employeeSickLeavesValues, storageItems, cancellationToken);
+ }
+ }
+ }
+
+ #endregion
+
+ #region internal class
+
+ private class EmployeeVacationsSynchronization : SharepointItemSynchronization
+ {
+ public EmployeeVacationsSynchronization(IExternalStorage externalStorage, ILogger? logger = null)
+ : base(externalStorage, logger)
+ {
+ }
+
+ protected override string ItemEventType { get; } = CalendarEventTypes.Vacation;
+
+ protected override DatesPeriod GetItemDatePeriod(VacationDescription item)
+ {
+ return new DatesPeriod(item.StartDate.Date, item.EndDate.Date);
+ }
+
+ protected override EmployeeId GetItemEmployeeId(VacationDescription item)
+ {
+ return item.EmployeeId;
+ }
+ }
+
+ private class EmployeeWorkoutsSynchronization : SharepointItemSynchronization
+ {
+ public EmployeeWorkoutsSynchronization(IExternalStorage externalStorage, ILogger? logger = null)
+ : base(externalStorage, logger)
+ {
+ }
+
+ protected override string ItemEventType { get; } = CalendarEventTypes.Workout;
+
+ protected override DatesPeriod GetItemDatePeriod(WorkHoursChange item)
+ {
+ return new DatesPeriod(item.Date, item.Date);
+ }
+
+ protected override EmployeeId GetItemEmployeeId(WorkHoursChange item)
+ {
+ return item.EmployeeId;
+ }
+ }
+
+ private class EmployeeSickLeavesSynchronization : SharepointItemSynchronization
+ {
+ public EmployeeSickLeavesSynchronization(IExternalStorage externalStorage, ILogger? logger = null)
+ : base(externalStorage, logger)
+ {
+ }
+
+ protected override string ItemEventType { get; } = CalendarEventTypes.Sickleave;
+
+ protected override DatesPeriod GetItemDatePeriod(SickLeaveDescription item)
+ {
+ return new DatesPeriod(item.StartDate.Date, item.EndDate.Date);
+ }
+
+ protected override EmployeeId GetItemEmployeeId(SickLeaveDescription item)
+ {
+ return item.EmployeeId;
+ }
+ }
+
+ #endregion
+
+ #region variables
+
+ private readonly ISickLeaves sickLeaves;
+ private readonly IVacations vacations;
+ private readonly IWorkHoursCredit workouts;
+ private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
+ private readonly ILogger? logger;
+
+ #endregion
+
+ #region private
+
+ private async Task> GetAllSharepointItemsForCalendar(IExternalStorage externalStorage, string calendar)
+ {
+ return await externalStorage.GetItems(calendar);
+ }
+
+ private IEnumerable GetSharepointCalendarsByDepartment(string departmentId)
+ {
+ return this.departmentsCalendarsSettings.DepartmentsCalendars
+ .Where(x => x.DepartmentId == departmentId)
+ .Select(x => x.Calendar);
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs
deleted file mode 100644
index 380591bcc..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/StoreCalendarEventToSharepoint.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Arcadia.Assistant.Sharepoint
-{
- using Calendar.Abstractions;
-
- using Employees.Contracts;
-
- public class StoreCalendarEventToSharepoint
- {
- public StoreCalendarEventToSharepoint(CalendarEvent @event, EmployeeMetadata employeeMetadata)
- {
- this.Event = @event;
- this.EmployeeMetadata = employeeMetadata;
- }
-
- public CalendarEvent Event { get; }
-
- public EmployeeMetadata EmployeeMetadata { get; }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.sln b/server2/Arcadia.Assistant/Arcadia.Assistant.sln
index f01d7caf9..ba512d951 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.sln
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.sln
@@ -75,8 +75,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.ExternalS
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.Sharepoint", "Arcadia.Assistant.Sharepoint\Arcadia.Assistant.Sharepoint.csproj", "{C5818540-DECF-409D-82CE-D34E72C34D7D}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcadia.Assistant.Calendar.Abstractions", "Arcadia.Assistant.Calendar.Abstractions\Arcadia.Assistant.Calendar.Abstractions.csproj", "{2D9BD0AE-9420-4743-96EF-C6F39F46C39F}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -341,14 +339,6 @@ Global
{C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|Any CPU.Build.0 = Release|Any CPU
{C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|x64.ActiveCfg = Release|Any CPU
{C5818540-DECF-409D-82CE-D34E72C34D7D}.Release|x64.Build.0 = Release|Any CPU
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|x64.ActiveCfg = Debug|x64
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Debug|x64.Build.0 = Debug|x64
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|Any CPU.Build.0 = Release|Any CPU
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|x64.ActiveCfg = Release|x64
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -374,7 +364,6 @@ Global
{9507586E-6D63-432E-99E2-59B73907ACA0} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
{085366A3-3D08-45B5-9A24-DDF3E1EEB2FA} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
{C5818540-DECF-409D-82CE-D34E72C34D7D} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
- {2D9BD0AE-9420-4743-96EF-C6F39F46C39F} = {EEF565D4-4A3D-4784-AE0F-AF39C4A27E90}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C376673D-F593-42B1-8093-D1F667D586B1}
From 832b65dc966981bf714399a806a8f15526191de6 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Wed, 25 Dec 2019 14:25:42 +0300
Subject: [PATCH 05/14] Remove redundant code
---
...dia.Assistant.Calendar.Abstractions.csproj | 12 --
.../CalendarEvent.cs | 38 ------
.../CalendarEventAdditionalDataEntry.cs | 15 ---
.../CalendarEventStatuses.cs | 113 ------------------
.../CalendarEventTypes.cs | 23 ----
.../CspCalendarEventIdParser.cs | 29 -----
.../DatesPeriod.cs | 104 ----------------
.../SickLeaveStatuses.cs | 17 ---
.../VacationStatuses.cs | 23 ----
.../WorkHoursChangeStatuses.cs | 19 ---
10 files changed, 393 deletions(-)
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj
deleted file mode 100644
index c9840b768..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/Arcadia.Assistant.Calendar.Abstractions.csproj
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
- netstandard2.1
- AnyCPU;x64
- enable
-
-
-
-
-
-
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs
deleted file mode 100644
index 111787f32..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEvent.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- using System.Linq;
-
- public class CalendarEvent
- {
- public CalendarEvent(
- string eventId,
- string type,
- DatesPeriod dates,
- string status,
- string employeeId,
- CalendarEventAdditionalDataEntry[]? additionalData = null)
- {
- this.EventId = eventId;
- this.Dates = dates;
- this.Status = status;
- this.Type = type;
- this.EmployeeId = employeeId;
- this.AdditionalData = additionalData ?? new CalendarEventAdditionalDataEntry[0];
- this.IsPending = new CalendarEventStatuses().PendingForType(type).Contains(status);
- }
-
- public string EventId { get; }
-
- public DatesPeriod Dates { get; }
-
- public string Status { get; }
-
- public string Type { get; }
-
- public bool IsPending { get; }
-
- public string EmployeeId { get; }
-
- public CalendarEventAdditionalDataEntry[] AdditionalData { get; }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs
deleted file mode 100644
index 932429b27..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventAdditionalDataEntry.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- public class CalendarEventAdditionalDataEntry
- {
- public CalendarEventAdditionalDataEntry(string key, string value)
- {
- this.Key = key;
- this.Value = value;
- }
-
- public string Key { get; }
-
- public string Value { get; }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs
deleted file mode 100644
index e61e5b356..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventStatuses.cs
+++ /dev/null
@@ -1,113 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- using System.Collections.Generic;
-
- public class CalendarEventStatuses
- {
- private static readonly IReadOnlyDictionary StatusesByType = new Dictionary
- {
- { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.All },
- { CalendarEventTypes.Workout, WorkHoursChangeStatuses.All },
- { CalendarEventTypes.Sickleave, SickLeaveStatuses.All },
- { CalendarEventTypes.Vacation, VacationStatuses.All }
- };
-
- private static readonly IReadOnlyDictionary PendingStatusesByType = new Dictionary
- {
- { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Pending },
- { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Pending },
- { CalendarEventTypes.Sickleave, SickLeaveStatuses.Pending },
- { CalendarEventTypes.Vacation, VacationStatuses.Pending }
- };
-
- private static readonly IReadOnlyDictionary ActualStatusesByType = new Dictionary
- {
- { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Actual },
- { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Actual },
- { CalendarEventTypes.Sickleave, SickLeaveStatuses.Actual },
- { CalendarEventTypes.Vacation, VacationStatuses.Actual }
- };
-
- private static readonly IReadOnlyDictionary ApprovedStatusByType = new Dictionary
- {
- { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Approved },
- { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Approved },
- { CalendarEventTypes.Vacation, VacationStatuses.Approved }
- };
-
- private static readonly IReadOnlyDictionary RejectedStatusByType = new Dictionary
- {
- { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Rejected },
- { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Rejected },
- { CalendarEventTypes.Vacation, VacationStatuses.Rejected }
- };
-
- private static readonly IReadOnlyDictionary CancelledStatusByType = new Dictionary
- {
- { CalendarEventTypes.Dayoff, WorkHoursChangeStatuses.Cancelled },
- { CalendarEventTypes.Workout, WorkHoursChangeStatuses.Cancelled },
- { CalendarEventTypes.Sickleave, SickLeaveStatuses.Cancelled },
- { CalendarEventTypes.Vacation, VacationStatuses.Cancelled }
- };
-
- public string[] AllForType(string type)
- {
- if (StatusesByType.TryGetValue(type, out var statuses))
- {
- return statuses;
- }
-
- return new string[0];
- }
-
- public string[] PendingForType(string type)
- {
- if (PendingStatusesByType.TryGetValue(type, out var statuses))
- {
- return statuses;
- }
-
- return new string[0];
- }
-
- public string[] ActualForType(string type)
- {
- if (ActualStatusesByType.TryGetValue(type, out var statuses))
- {
- return statuses;
- }
-
- return new string[0];
- }
-
- public string? ApprovedForType(string type)
- {
- if (ApprovedStatusByType.TryGetValue(type, out var status))
- {
- return status;
- }
-
- return null;
- }
-
- public string? RejectedForType(string type)
- {
- if (RejectedStatusByType.TryGetValue(type, out var status))
- {
- return status;
- }
-
- return null;
- }
-
- public string? CancelledForType(string type)
- {
- if (CancelledStatusByType.TryGetValue(type, out var status))
- {
- return status;
- }
-
- return null;
- }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs
deleted file mode 100644
index 066fb7ce3..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CalendarEventTypes.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- using System;
- using System.Linq;
-
- public static class CalendarEventTypes
- {
- public const string Vacation = "Vacation";
-
- public const string Dayoff = "Dayoff";
-
- public const string Workout = "Workout";
-
- public const string Sickleave = "Sickleave";
-
- public static readonly string[] All = { Vacation, Dayoff, Workout, Sickleave };
-
- public static bool IsKnownType(string x)
- {
- return All.Contains(x, StringComparer.InvariantCultureIgnoreCase);
- }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs
deleted file mode 100644
index 7107a2dec..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/CspCalendarEventIdParser.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- using System;
-
- public static class CspCalendarEventIdParser
- {
- public static int GetCspIdFromCalendarEvent(string calendarEventId, string calendarEventType)
- {
- var parts = calendarEventId.Split('_');
-
- if (parts.Length != 2 || parts[0] != calendarEventType || !int.TryParse(parts[1], out var cspId))
- {
- throw new ArgumentException("Calendar event id has wrong format");
- }
-
- return cspId;
- }
-
- public static string GetCalendarEventIdFromCspId(int cspId, string calendarEventType)
- {
- return $"{calendarEventType}_{cspId}";
- }
-
- public static string GetCalendarEventIdFromCspId(Guid cspId, string calendarEventType)
- {
- return $"{calendarEventType}_{cspId}";
- }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs
deleted file mode 100644
index 4dbcaaf43..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/DatesPeriod.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- using System;
-
- public sealed class DatesPeriod
- {
- public DatesPeriod(DateTime startDate, DateTime endDate, int startWorkingHour = 0, int finishWorkingHour = 8)
- {
- this.StartDate = MinDate(startDate, endDate);
- this.EndDate = MaxDate(startDate, endDate);
- this.StartWorkingHour = Math.Min(startWorkingHour, finishWorkingHour);
- this.FinishWorkingHour = Math.Max(startWorkingHour, finishWorkingHour);
- }
-
- public DateTime StartDate { get; }
-
- public DateTime EndDate { get; }
-
- ///
- /// Starting working hour index. Typically, 0 or 4.
- ///
- public int StartWorkingHour { get; }
-
- ///
- /// Finish working hour index. Typically, 4 or 8
- ///
- public int FinishWorkingHour { get; }
-
- private bool Equals(DatesPeriod other)
- {
- return this.StartDate.Equals(other.StartDate)
- && this.EndDate.Equals(other.EndDate)
- && this.StartWorkingHour == other.StartWorkingHour
- && this.FinishWorkingHour == other.FinishWorkingHour;
- }
-
- public override bool Equals(object obj)
- {
- if (ReferenceEquals(null, obj))
- {
- return false;
- }
-
- if (ReferenceEquals(this, obj))
- {
- return true;
- }
-
- if (obj.GetType() != this.GetType())
- {
- return false;
- }
-
- return this.Equals((DatesPeriod)obj);
- }
-
- public override int GetHashCode()
- {
- unchecked
- {
- var hashCode = this.StartDate.GetHashCode();
- hashCode = (hashCode * 397) ^ this.EndDate.GetHashCode();
- hashCode = (hashCode * 397) ^ this.StartWorkingHour;
- hashCode = (hashCode * 397) ^ this.FinishWorkingHour;
- return hashCode;
- }
- }
-
- public static bool operator ==(DatesPeriod left, DatesPeriod right)
- {
- return Equals(left, right);
- }
-
- public static bool operator !=(DatesPeriod left, DatesPeriod right)
- {
- return !Equals(left, right);
- }
-
- public bool DatesIntersectsWith(DatesPeriod? period)
- {
- if (period == null)
- {
- return false;
- }
-
- if (this.EndDate < period.StartDate || period.EndDate < this.StartDate)
- {
- return false;
- }
-
- return true;
- }
-
- private static DateTime MinDate(DateTime first, DateTime second)
- {
- return first <= second ? first : second;
- }
-
- private static DateTime MaxDate(DateTime first, DateTime second)
- {
- return first >= second ? first : second;
- }
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs
deleted file mode 100644
index e7596fa40..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/SickLeaveStatuses.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- public static class SickLeaveStatuses
- {
- public const string Requested = "Requested";
-
- public const string Cancelled = "Cancelled";
-
- public const string Completed = "Completed";
-
- public static readonly string[] All = { Requested, Completed, Cancelled };
-
- public static readonly string[] Pending = { Requested };
-
- public static readonly string[] Actual = { Requested, Completed };
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs
deleted file mode 100644
index a55c1e6ee..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/VacationStatuses.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- public static class VacationStatuses
- {
- public const string Requested = "Requested";
-
- public const string Cancelled = "Cancelled";
-
- public const string Approved = "Approved";
-
- public const string Rejected = "Rejected";
-
- public const string AccountingReady = "AccountingReady";
-
- public const string Processed = "Processed";
-
- public static readonly string[] All = { Requested, Approved, Cancelled, Rejected, AccountingReady, Processed };
-
- public static readonly string[] Pending = { Requested };
-
- public static readonly string[] Actual = { Requested, Approved, AccountingReady, AccountingReady, Processed };
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs
deleted file mode 100644
index f0076ecce..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Calendar.Abstractions/WorkHoursChangeStatuses.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Arcadia.Assistant.Calendar.Abstractions
-{
- public static class WorkHoursChangeStatuses
- {
- public const string Requested = "Requested";
-
- public const string Cancelled = "Cancelled";
-
- public const string Approved = "Approved";
-
- public const string Rejected = "Rejected";
-
- public static readonly string[] All = { Requested, Approved, Cancelled, Rejected };
-
- public static readonly string[] Pending = { Requested };
-
- public static readonly string[] Actual = { Requested, Approved };
- }
-}
\ No newline at end of file
From 4e85f4f42f1fa516e33b4c77acd7f932c0bc5c10 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Wed, 25 Dec 2019 14:41:15 +0300
Subject: [PATCH 06/14] Fix method name
---
.../Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs | 4 ++--
.../Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs | 2 +-
.../Arcadia.Assistant.SickLeaves/SickLeaves.cs | 2 +-
.../IWorkHoursCredit.cs | 2 +-
.../Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs | 2 +-
5 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
index 2c28ee474..d3293c4e8 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
@@ -54,10 +54,10 @@ public async Task Synchronize(IEmployees employees, IEnumerable departme
var employeeVacations = await this.vacations.GetCalendarEventsByEmployeeAsync(employeeIds, cancellationToken);
var employeeVacationValues = employeeVacations.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
- var employeeWorkouts = await this.workouts.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
+ var employeeWorkouts = await this.workouts.GetCalendarEventsByEmployeeMapAsync(employeeIds, cancellationToken);
var employeeWorkoutsValues = employeeWorkouts.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
- var employeeSickLeaves = await this.sickLeaves.GetCalendarEventsCollectionAsync(employeeIds, cancellationToken);
+ var employeeSickLeaves = await this.sickLeaves.GetCalendarEventsByEmployeeMapAsync(employeeIds, cancellationToken);
var employeeSickLeavesValues = employeeSickLeaves.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
foreach (var calendar in this.GetSharepointCalendarsByDepartment(departmentId))
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs
index 915b1f1bf..f94ecbc94 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves.Contracts/ISickLeaves.cs
@@ -18,7 +18,7 @@ public interface ISickLeaves : IService
{
Task GetCalendarEventsAsync(EmployeeId employeeId, CancellationToken cancellationToken);
- Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken);
+ Task> GetCalendarEventsByEmployeeMapAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken);
Task GetCalendarEventAsync(EmployeeId employeeId, int eventId, CancellationToken cancellationToken);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs
index baa621881..f73928dee 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/SickLeaves.cs
@@ -80,7 +80,7 @@ public async Task GetCalendarEventsAsync(EmployeeId empl
return sickLeaves;
}
- public async Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken)
+ public async Task> GetCalendarEventsByEmployeeMapAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken)
{
using var db = this.dbFactory();
var sickLeaves = await db.Value
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs
index 4d58f9fc3..7c539f356 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit.Contracts/IWorkHoursCredit.cs
@@ -24,7 +24,7 @@ public interface IWorkHoursCredit : IService
Task GetCalendarEventsAsync(EmployeeId employeeId, CancellationToken cancellationToken);
- Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken);
+ Task> GetCalendarEventsByEmployeeMapAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken);
Task GetCalendarEventAsync(EmployeeId employeeId, Guid eventId, CancellationToken cancellationToken);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
index 59e5056a0..7a7571889 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
@@ -77,7 +77,7 @@ public async Task GetCalendarEventsAsync(EmployeeId employeeI
return events;
}
- public async Task> GetCalendarEventsCollectionAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken)
+ public async Task> GetCalendarEventsByEmployeeMapAsync(EmployeeId[] employeeIds, CancellationToken cancellationToken)
{
using var ctx = this.dbFactory();
var events = await this.QueryCalendarEvents(ctx.Value.ChangeRequests, x => employeeIds.Any(id => id.Value == x.EmployeeId))
From 48cd463bcc13d75b8ff7db6027b795d2ab6ed1d2 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Tue, 21 Jan 2020 18:41:16 +0300
Subject: [PATCH 07/14] Fix list item to Json converting
---
...stant.ExternalStorages.Abstractions.csproj | 1 +
.../PropertyNameParser.cs | 4 +-
.../SharepointStorageItemComparer.cs | 10 +-
.../StorageItem.cs | 11 +-
.../Contracts/ISharepointFieldsMapper.cs | 2 +
.../Contracts/SharepointRequest.cs | 28 ++++-
.../SharepointApiModels/SharepointListItem.cs | 33 -----
.../SharepointListItemRequest.cs | 53 ++++----
.../SharepointListItemsResponse.cs | 5 +-
.../SharepointAuthTokenService.cs | 5 +
.../SharepointFieldsMapper.cs | 12 +-
.../SharepointStorage.cs | 97 ++++----------
.../StorageItemJsonConvertor.cs | 119 ++++++++++++++++++
.../SharepointItemSynchronization.cs | 30 ++---
14 files changed, 246 insertions(+), 164 deletions(-)
delete mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs
create mode 100644 server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj
index 3059692d3..1a41ab789 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/Arcadia.Assistant.ExternalStorages.Abstractions.csproj
@@ -8,6 +8,7 @@
+
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs
index a76a6389e..5527202c1 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/PropertyNameParser.cs
@@ -6,12 +6,12 @@
public class PropertyNameParser
{
- public void EnsureExpressionIsProperty(Expression> expression)
+ public void EnsureExpressionIsProperty(Expression> expression)
{
this.GetName(expression);
}
- public string GetName(Expression> expression)
+ public string GetName(Expression> expression)
{
switch (expression.Body)
{
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/SharepointStorageItemComparer.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/SharepointStorageItemComparer.cs
index 2710c3aa0..1380a9a05 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/SharepointStorageItemComparer.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/SharepointStorageItemComparer.cs
@@ -27,14 +27,14 @@ public bool Equals(StorageItem x, StorageItem y)
}
return
- string.Equals(x.Id, y.Id) &&
- string.Equals(x.Title, y.Title) &&
- string.Equals(x.Description, y.Description) &&
+ x.Id.Equals(y.Id) &&
+ x.Title.Equals(y.Title) &&
+ x.Description.Equals(y.Description) &&
x.StartDate.Date.Equals(y.StartDate.Date) &&
x.EndDate.Date.Equals(y.EndDate.Date) &&
- string.Equals(x.Category, y.Category) &&
+ x.Category.Equals(y.Category) &&
x.AllDayEvent == y.AllDayEvent &&
- string.Equals(x.CalendarEventId, y.CalendarEventId);
+ x.CalendarEventId.Equals(y.CalendarEventId);
}
public int GetHashCode(StorageItem obj)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs
index 30dace379..37a0932a4 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.Abstractions/StorageItem.cs
@@ -1,23 +1,32 @@
namespace Arcadia.Assistant.ExternalStorages.Abstractions
{
using System;
+ using System.Text.Json.Serialization;
public class StorageItem
{
- public string Id { get; set; } = string.Empty;
+ [JsonPropertyName("Id")]
+ public int Id { get; set; }
+ [JsonPropertyName("Title")]
public string Title { get; set; } = string.Empty;
+ [JsonPropertyName("Description")]
public string Description { get; set; } = string.Empty;
+ [JsonPropertyName("StartDate")]
public DateTime StartDate { get; set; }
+ [JsonPropertyName("EndDate")]
public DateTime EndDate { get; set; }
+ [JsonPropertyName("Category")]
public string Category { get; set; } = string.Empty;
+ [JsonPropertyName("AllDayEvent")]
public bool AllDayEvent { get; set; }
+ [JsonPropertyName("CalendarEventId")]
public string CalendarEventId { get; set; } = string.Empty;
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
index f27d2ffb8..b937bd8cb 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
@@ -8,5 +8,7 @@
public interface ISharepointFieldsMapper
{
string GetSharepointField(Expression> property);
+
+ string? GetSharepointField(string propertyName);
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
index 46980fdb1..5e667e080 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
@@ -2,6 +2,7 @@
{
using System;
using System.Collections.Generic;
+ using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
@@ -36,9 +37,18 @@ public static SharepointRequest Create(HttpMethod httpMethod, string url)
return new SharepointRequest(httpMethod, url);
}
- public SharepointRequest WithAcceptHeader(string value)
+ public SharepointRequest WithAcceptHeader(string value, bool update = true)
{
- this.AddHeader(AcceptHeaderName, value);
+ if (update)
+ {
+ this.RemoveHeader(AcceptHeaderName);
+ }
+
+ if (update || !this.headersInternal.Any(x => x.Item1 == AcceptHeaderName))
+ {
+ this.AddHeader(AcceptHeaderName, value);
+ }
+
return this;
}
@@ -48,9 +58,8 @@ public SharepointRequest WithBearerAuthorizationHeader(string value)
return this;
}
- public SharepointRequest WithContent(object content)
+ public SharepointRequest WithContentString(string contentString)
{
- var contentString = JsonSerializer.Serialize(content);
this.Content = new StringContent(contentString);
this.AddHeader(ContentTypeHeaderName, "application/json;odata=verbose");
@@ -59,6 +68,12 @@ public SharepointRequest WithContent(object content)
return this;
}
+ public SharepointRequest WithContent(object content)
+ {
+ var contentString = JsonSerializer.Serialize(content);
+ return this.WithContentString(contentString);
+ }
+
public SharepointRequest WithIfMatchHeader()
{
this.AddHeader(IfMatchHeaderName, "*");
@@ -115,5 +130,10 @@ private void AddHeader(string name, string value)
{
this.headersInternal.Add(Tuple.Create(name, value));
}
+
+ private void RemoveHeader(string name)
+ {
+ this.headersInternal.Where(x => x.Item1 == name).ToList().ForEach(x => this.headersInternal.Remove(x));
+ }
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs
deleted file mode 100644
index dc82209f6..000000000
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItem.cs
+++ /dev/null
@@ -1,33 +0,0 @@
-namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.SharepointApiModels
-{
- using System;
- using System.Runtime.Serialization;
-
- [DataContract]
- public class SharepointListItem
- {
- [DataMember]
- public int Id { get; set; }
-
- [DataMember]
- public string Title { get; set; } = string.Empty;
-
- [DataMember]
- public string Description { get; set; } = string.Empty;
-
- [DataMember]
- public DateTime EventDate { get; set; }
-
- [DataMember]
- public DateTime EndDate { get; set; }
-
- [DataMember]
- public string Category { get; set; } = string.Empty;
-
- [DataMember(Name = "fAllDayEvent")]
- public bool AllDayEvent { get; set; }
-
- [DataMember]
- public string CalendarEventId { get; set; } = string.Empty;
- }
-}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
index 07f956911..64b5c6d80 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
@@ -2,39 +2,42 @@
{
using System.Runtime.Serialization;
+ using Abstractions;
+
[DataContract]
- public class SharepointListItemRequest
+ public class SharepointListItemRequest : StorageItem
{
+ public SharepointListItemRequest(StorageItem storage, string? type = null)
+ {
+ this.Id = storage.Id;
+ this.Title = storage.Title;
+ this.Description = storage.Description;
+ this.StartDate = storage.StartDate;
+ this.EndDate = storage.EndDate;
+ this.Category = storage.Category;
+ this.AllDayEvent = storage.AllDayEvent;
+ this.CalendarEventId = storage.CalendarEventId;
+ if (type != null)
+ {
+ this.Metadata = new MetadataRequest(type);
+ }
+ }
+
[DataMember(Name = "__metadata")]
public MetadataRequest Metadata { get; set; } = new MetadataRequest();
- [DataMember]
- public int Id { get; set; }
-
- [DataMember]
- public string Title { get; set; } = string.Empty;
-
- [DataMember]
- public string Description { get; set; } = string.Empty;
-
- [DataMember]
- public string EventDate { get; set; } = string.Empty;
-
- [DataMember]
- public string EndDate { get; set; } = string.Empty;
-
- [DataMember]
- public string Category { get; set; } = string.Empty;
-
- [DataMember(Name = "fAllDayEvent")]
- public bool AllDayEvent { get; set; }
-
- [DataMember]
- public string CalendarEventId { get; set; } = string.Empty;
-
[DataContract]
public class MetadataRequest
{
+ public MetadataRequest()
+ {
+ }
+
+ public MetadataRequest(string type)
+ {
+ this.Type = type;
+ }
+
[DataMember(Name = "type")]
public string? Type { get; set; }
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs
index 2ea6c9070..0c93701b3 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemsResponse.cs
@@ -1,9 +1,12 @@
namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.SharepointApiModels
{
using System.Collections.Generic;
+ using System.Text.Json;
+ using System.Text.Json.Serialization;
public class SharepointListItemsResponse
{
- public IEnumerable? Value { get; set; }
+ [JsonPropertyName("value")]
+ public IEnumerable? Value { get; set; }
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
index 22d317554..8e8d2e188 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
@@ -9,6 +9,7 @@
using System.Net.Http.Headers;
using System.Runtime.Serialization;
using System.Text.Json;
+ using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
@@ -185,6 +186,7 @@ private string GetFormattedPrincipal(string principalName, string? hostName, str
[DataContract]
private class JsonMetadataDocument
{
+ [JsonPropertyName("endpoints")]
[DataMember(Name = "endpoints")]
public IEnumerable? Endpoints { get; set; }
}
@@ -192,9 +194,11 @@ private class JsonMetadataDocument
[DataContract]
private class JsonEndpoint
{
+ [JsonPropertyName("location")]
[DataMember(Name = "location")]
public string? Location { get; set; }
+ [JsonPropertyName("protocol")]
[DataMember(Name = "protocol")]
public string? Protocol { get; set; }
}
@@ -202,6 +206,7 @@ private class JsonEndpoint
[DataContract]
private class OAuth2AccessTokenResponse
{
+ [JsonPropertyName("access_token")]
[DataMember(Name = "access_token")]
public string? AccessToken { get; set; }
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
index a9241e03f..a11f0aefb 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
@@ -43,13 +43,19 @@ public SharepointFieldsMapper(params SharepointFieldMapping[] fields)
public string GetSharepointField(Expression> property)
{
var propertyName = this.GetPropertyName(property);
+ var fieldName = this.GetSharepointField(propertyName);
- if (this.fieldsByPropertyName.TryGetValue(propertyName, out var field))
+ if (fieldName == null)
{
- return field;
+ throw new ArgumentException($"Mapping for property '{propertyName}' doesn't exist");
}
- throw new ArgumentException($"Mapping for property '{propertyName}' doesn't exist");
+ return fieldName;
+ }
+
+ public string? GetSharepointField(string propertyName)
+ {
+ return this.fieldsByPropertyName.TryGetValue(propertyName, out var field) ? field : null;
}
public static SharepointFieldMapping CreateMapping(Expression> property, string fieldName)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
index bf8c7d502..251bb0385 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
@@ -17,7 +17,6 @@ public class SharepointStorage : IExternalStorage
{
private readonly ISharepointConditionsCompiler conditionsCompiler;
private readonly ISharepointOnlineConfiguration configuration;
- private readonly ISharepointFieldsMapper fieldsMapper;
private readonly ISharepointRequestExecutor requestExecutor;
public SharepointStorage(
@@ -28,31 +27,33 @@ public SharepointStorage(
{
this.configuration = configuration;
this.requestExecutor = requestExecutor;
- this.fieldsMapper = sharePointFieldsMapper;
+ this.FieldsMapper = sharePointFieldsMapper;
this.conditionsCompiler = sharePointConditionsCompiler;
+ this.StorageItemJsonConvertor = new StorageItemJsonConvertor(this.FieldsMapper);
}
+ private ISharepointFieldsMapper FieldsMapper { get; }
+
+ private StorageItemJsonConvertor StorageItemJsonConvertor { get; }
+
public async Task> GetItems(string list, IEnumerable? conditions, CancellationToken cancellationToken)
{
var listItems = await this.GetListItems(list, conditions, cancellationToken);
- return listItems
- .Select(this.ListItemToStorageItem)
- .ToArray();
+ return listItems?.ToArray() ?? new StorageItem[0];
}
public async Task AddItem(string list, StorageItem item, CancellationToken cancellationToken)
{
var listItemType = await this.GetListItemType(list, cancellationToken);
- var requestData = this.StorageItemToListItemRequest(item, listItemType);
+ var requestData = this.StorageItemJsonConvertor.StorageItemToRequestJson(item, listItemType);
var request = SharepointRequest
- .Create(HttpMethod.Post, this.GetListItemsUrl(list))
- .WithContent(requestData);
+ .Create(HttpMethod.Post, this.GetListItemsUrl(list, false))
+ .WithAcceptHeader("application/json;odata=verbose")
+ .WithContentString(requestData);
- var addedItem = await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
-
- return this.ListItemToStorageItem(addedItem);
+ return await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
}
public async Task UpdateItem(string list, StorageItem item, CancellationToken cancellationToken)
@@ -69,15 +70,16 @@ public async Task UpdateItem(string list, StorageItem item, CancellationToken ca
var listItemType = await this.GetListItemType(list, cancellationToken);
- var requestData = this.StorageItemToListItemRequest(item, listItemType);
+ var requestData = this.StorageItemJsonConvertor.StorageItemToRequestJson(item, listItemType);
var request = SharepointRequest
.Create(HttpMethod.Post, updateItemUrl)
- .WithContent(requestData)
+ .WithAcceptHeader("application/json;odata=verbose")
+ .WithContentString(requestData)
.WithIfMatchHeader()
.WithXHttpMethodHeader("MERGE");
- await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
+ await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
}
public async Task DeleteItem(string list, string itemId, CancellationToken cancellationToken)
@@ -94,6 +96,7 @@ public async Task DeleteItem(string list, string itemId, CancellationToken cance
var request = SharepointRequest
.Create(HttpMethod.Post, deleteItemUrl)
+ .WithAcceptHeader("application/json;odata=verbose")
.WithIfMatchHeader()
.WithXHttpMethodHeader("DELETE");
@@ -104,7 +107,7 @@ public void Dispose()
{
}
- private async Task?> GetListItems(
+ private async Task?> GetListItems(
string list,
IEnumerable? conditions,
CancellationToken cancellationToken)
@@ -120,8 +123,7 @@ public void Dispose()
var request = SharepointRequest.Create(HttpMethod.Get, itemsUrl);
var response = await this.requestExecutor.ExecuteSharepointRequest(request, cancellationToken);
-
- return response.Value;
+ return response.Value.Select(this.StorageItemJsonConvertor.JsonToStorageItem);
}
private async Task GetListItemType(string list, CancellationToken cancellationToken)
@@ -134,14 +136,14 @@ public void Dispose()
private string GetListUrl(string list)
{
- return $"{this.configuration.ServerUrl}/_api/lists/GetByTitle('{list}')";
+ return $"{this.configuration.ServerUrl}/_api/web/lists/getByTitle('{list}')";
}
private string GetListItemsUrl(string list, bool includeSelectPart = true)
{
var baseUrl = $"{this.GetListUrl(list)}/items";
- var fieldNames = this.GetFieldNames();
+ var fieldNames = this.StorageItemJsonConvertor.GetFieldNames();
var selectUrlPart =
$"$select={fieldNames.Id},{fieldNames.Title},{fieldNames.Description},{fieldNames.StartDate}," +
@@ -152,40 +154,7 @@ private string GetListItemsUrl(string list, bool includeSelectPart = true)
: $"{baseUrl}?{selectUrlPart}";
}
- private StorageItem ListItemToStorageItem(SharepointListItem item)
- {
- return new StorageItem
- {
- Id = item.Id.ToString(),
- Title = item.Title,
- Description = item.Description,
- StartDate = item.EventDate,
- EndDate = item.EndDate,
- Category = item.Category,
- AllDayEvent = item.AllDayEvent,
- CalendarEventId = item.CalendarEventId
- };
- }
-
- private SharepointListItemRequest StorageItemToListItemRequest(StorageItem item, string? listItemType)
- {
- return new SharepointListItemRequest
- {
- Metadata = new SharepointListItemRequest.MetadataRequest
- {
- Type = listItemType
- },
- Title = item.Title,
- Description = item.Description,
- EventDate = item.StartDate.ToString("d"),
- EndDate = item.EndDate.ToString("d"),
- Category = item.Category,
- AllDayEvent = item.AllDayEvent,
- CalendarEventId = item.CalendarEventId
- };
- }
-
- private void EnsureSingleItemReturned(SharepointListItem[] listItems)
+ private void EnsureSingleItemReturned(StorageItem[] listItems)
{
if (listItems.Length == 0)
{
@@ -197,27 +166,5 @@ private void EnsureSingleItemReturned(SharepointListItem[] listItems)
throw new ArgumentException("More than one item was found by specified conditions");
}
}
-
- private (
- string Id,
- string Title,
- string Description,
- string StartDate,
- string EndDate,
- string Category,
- string AllDayEvent,
- string CalendarEventId
- ) GetFieldNames()
- {
- var idField = this.fieldsMapper.GetSharepointField(si => si.Id);
- var titleField = this.fieldsMapper.GetSharepointField(si => si.Title);
- var descriptionField = this.fieldsMapper.GetSharepointField(si => si.Description);
- var startDateField = this.fieldsMapper.GetSharepointField(si => si.StartDate);
- var endDateField = this.fieldsMapper.GetSharepointField(si => si.EndDate);
- var categoryField = this.fieldsMapper.GetSharepointField(si => si.Category);
- var allDayEvent = this.fieldsMapper.GetSharepointField(si => si.AllDayEvent);
- var calendarEventIdField = this.fieldsMapper.GetSharepointField(si => si.CalendarEventId);
- return (idField, titleField, descriptionField, startDateField, endDateField, categoryField, allDayEvent, calendarEventIdField);
- }
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs
new file mode 100644
index 000000000..33389cf3c
--- /dev/null
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs
@@ -0,0 +1,119 @@
+namespace Arcadia.Assistant.ExternalStorages.SharepointOnline
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text.Json;
+
+ using Abstractions;
+
+ using Contracts;
+
+ public class StorageItemJsonConvertor
+ {
+ public StorageItemJsonConvertor(ISharepointFieldsMapper fieldsMapper)
+ {
+ this.FieldsMapper = fieldsMapper;
+ }
+
+ private ISharepointFieldsMapper FieldsMapper { get; }
+
+ #region public interface
+
+ public (
+ string Id,
+ string Title,
+ string Description,
+ string StartDate,
+ string EndDate,
+ string Category,
+ string AllDayEvent,
+ string CalendarEventId
+ ) GetFieldNames()
+ {
+ var idField = this.FieldsMapper.GetSharepointField(si => si.Id);
+ var titleField = this.FieldsMapper.GetSharepointField(si => si.Title);
+ var descriptionField = this.FieldsMapper.GetSharepointField(si => si.Description);
+ var startDateField = this.FieldsMapper.GetSharepointField(si => si.StartDate);
+ var endDateField = this.FieldsMapper.GetSharepointField(si => si.EndDate);
+ var categoryField = this.FieldsMapper.GetSharepointField(si => si.Category);
+ var allDayEvent = this.FieldsMapper.GetSharepointField(si => si.AllDayEvent);
+ var calendarEventIdField = this.FieldsMapper.GetSharepointField(si => si.CalendarEventId);
+ return (idField, titleField, descriptionField, startDateField, endDateField, categoryField, allDayEvent, calendarEventIdField);
+ }
+
+ public string StorageItemToRequestJson(StorageItem item, string? listItemType)
+ {
+ var properties = new Dictionary
+ {
+ [$"{this.FieldsMapper.GetSharepointField(x => x.Title)}"] = $"\"{item.Title}\"",
+ [$"{this.FieldsMapper.GetSharepointField(x => x.Description)}"] = $"\"{item.Description}\"",
+ [$"{this.FieldsMapper.GetSharepointField(x => x.StartDate)}"] = $"\"{item.StartDate.ToString("d")}\"",
+ [$"{this.FieldsMapper.GetSharepointField(x => x.EndDate)}"] = $"\"{item.EndDate.ToString("d")}\"",
+ [$"{this.FieldsMapper.GetSharepointField(x => x.Category)}"] = $"\"{item.Category}\"",
+ [$"{this.FieldsMapper.GetSharepointField(x => x.AllDayEvent)}"] = item.AllDayEvent ? "true" : "false",
+ [$"{this.FieldsMapper.GetSharepointField(x => x.CalendarEventId)}"] = $"\"{item.CalendarEventId}\""
+ };
+
+ if (item.Id != 0)
+ {
+ properties.Add($"{this.FieldsMapper.GetSharepointField(x => x.Id)}", item.Id.ToString());
+ }
+
+ if (listItemType != null)
+ {
+ properties.Add("__metadata", $"{{ \"type\":\"{listItemType}\" }}");
+ }
+
+ return $"{{ {string.Join(',', properties.Keys.Select(k => $"\"{k}\":{properties[k]}"))} }}";
+ }
+
+ public StorageItem? JsonToStorageItem(JsonElement item)
+ {
+ var allDayEvent = this.GetJsonBool(item, this.FieldsMapper.GetSharepointField(x => x.AllDayEvent));
+ return new StorageItem
+ {
+ Id = this.GetJsonInt(item, this.FieldsMapper.GetSharepointField(x => x.Id)),
+ Title = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.Title)),
+ Description = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.Description)),
+ StartDate = this.GetJsonDate(item, this.FieldsMapper.GetSharepointField(x => x.StartDate), allDayEvent, DateTime.MinValue),
+ EndDate = this.GetJsonDate(item, this.FieldsMapper.GetSharepointField(x => x.EndDate), allDayEvent, DateTime.UtcNow),
+ Category = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.Category)),
+ AllDayEvent = allDayEvent,
+ CalendarEventId = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.CalendarEventId))
+ };
+ }
+
+ #endregion
+
+ #region private members
+
+ private string GetJsonString(JsonElement item, string propName, string defaultValue = "")
+ {
+ return item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null ? prop.GetString() : defaultValue;
+ }
+
+ private int GetJsonInt(JsonElement item, string propName, int defaultValue = 0)
+ {
+ return item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null && prop.TryGetInt32(out var val) ? val : defaultValue;
+ }
+
+ private bool GetJsonBool(JsonElement item, string propName, bool defaultValue = false)
+ {
+ return item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null ? prop.GetBoolean() : defaultValue;
+ }
+
+ private DateTime GetJsonDate(JsonElement item, string propName, bool dateOnly, DateTime defaultValue)
+ {
+ var date = item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null && prop.TryGetDateTime(out var val) ? val : defaultValue;
+ return dateOnly ? new DateTime(date.Year, date.Month, date.Day) : date;
+ }
+
+ private DateTime GetJsonDate(JsonElement item, string propName, DateTime defaultValue)
+ {
+ return item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null && prop.TryGetDateTime(out var val) ? val : defaultValue;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
index 26c880b5b..89e35f846 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
@@ -47,7 +47,7 @@ public async Task SynchronizeItem(string calendar, EmployeeMetadata[] department
{
await this.ExternalStorage.DeleteItem(
calendar,
- item.Id);
+ item.Id.ToString());
}
}
catch (Exception e)
@@ -81,7 +81,7 @@ await this.ExternalStorage.DeleteItem(
private async Task UpsertItem(string eventId, string calendar, T item, EmployeeMetadata employeeMetadata, IExternalStorage externalStorage, CancellationToken cancellationToken)
{
var datesPeriod = this.GetItemDatePeriod(item);
- var upsertItem = this.CalendarEventToStorageItem(eventId, CalendarEventTypes.Sickleave, datesPeriod, employeeMetadata);
+ var upsertItem = this.CalendarEventToStorageItem(eventId, this.ItemEventType, datesPeriod, employeeMetadata);
await this.UpsertStorageItem(eventId, calendar, datesPeriod, upsertItem, externalStorage, cancellationToken);
}
@@ -104,26 +104,26 @@ await externalStorage.AddItem(
upsertItem,
cancellationToken);
}
- else if (!this.sharepointStorageItemComparer.Equals(upsertItem, storageItem))
+ else
{
upsertItem.Id = storageItem.Id;
- await externalStorage.UpdateItem(
- calendar,
- upsertItem,
- cancellationToken);
+ if (!this.sharepointStorageItemComparer.Equals(upsertItem, storageItem))
+ {
+ await externalStorage.UpdateItem(
+ calendar,
+ upsertItem,
+ cancellationToken);
+ }
}
}
private StorageItem CalendarEventToStorageItem(string eventId, string calendarEventType, DatesPeriod period, EmployeeMetadata employeeMetadata)
{
var totalHours = period.FinishWorkingHour - period.StartWorkingHour;
-
- var longEventsTitle = $"{employeeMetadata.Name} ({calendarEventType})";
- var shortEventsTitle = $"{employeeMetadata.Name} ({calendarEventType}: {totalHours} hours)";
-
- var title = calendarEventType == CalendarEventTypes.Vacation || calendarEventType == CalendarEventTypes.Sickleave
- ? longEventsTitle
- : shortEventsTitle;
+ var longEvent = calendarEventType == CalendarEventTypes.Vacation || calendarEventType == CalendarEventTypes.Sickleave;
+ var title = longEvent
+ ? $"{employeeMetadata.Name} ({calendarEventType})"
+ : $"{employeeMetadata.Name} ({calendarEventType}: {totalHours} hours)";
var storageItem = new StorageItem
{
@@ -131,7 +131,7 @@ private StorageItem CalendarEventToStorageItem(string eventId, string calendarEv
StartDate = period.StartDate,
EndDate = period.EndDate,
Category = calendarEventType,
- AllDayEvent = true,
+ AllDayEvent = longEvent,
CalendarEventId = eventId
};
From 87ece52ebb8ecdcf89e94a83f857d0bcfa8ce5a7 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Tue, 11 Feb 2020 17:13:41 +0300
Subject: [PATCH 08/14] Fixed merge result
---
.../Program.cs | 2 +-
.../Program.cs | 2 +-
.../Arcadia.Assistant.Avatars/Program.cs | 2 +-
.../Arcadia.Assistant.Employees/Program.cs | 2 +-
.../Arcadia.Assistant.Inbox/Program.cs | 2 +-
.../LoggerRegistrationExtensions.cs | 21 ++++++++-----
.../LoggerSettings.cs | 4 +--
.../Arcadia.Assistant.MobileBuild/Program.cs | 2 +-
.../Arcadia.Assistant.Organization/Program.cs | 2 +-
.../Program.cs | 2 +-
.../Arcadia.Assistant.Permissions/Program.cs | 2 +-
.../ApplicationManifest.xml | 31 ++++++++++---------
.../Arcadia.Assistant.Sharepoint.csproj | 1 +
.../Arcadia.Assistant.Sharepoint/Program.cs | 18 ++++++-----
.../Arcadia.Assistant.SickLeaves/Program.cs | 2 +-
.../Program.cs | 2 +-
.../Arcadia.Assistant.Vacations/Program.cs | 2 +-
.../Program.cs | 2 +-
.../Program.cs | 2 +-
19 files changed, 58 insertions(+), 45 deletions(-)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs
index fab8db426..0c876f5e0 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(AppCenterBuilds).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs
index 3be7adea2..d58e96e31 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs
@@ -48,7 +48,7 @@ private static void Main()
builder.RegisterStatelessService("Arcadia.Assistant.Avatars.ManagerType");
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(Manager).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs
index 9ef60f9a0..4b08e0505 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs
@@ -30,7 +30,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.Resolve();
+ logger = container.TryResolve(out ILogger val) ? val : null;
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs
index 3752428e2..3b8e4085b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs
@@ -39,7 +39,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(Employees).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs
index 011d18996..26fa18689 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs
@@ -30,7 +30,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(Inbox).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
Thread.Sleep(Timeout.Infinite);
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerRegistrationExtensions.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerRegistrationExtensions.cs
index 6531b42cf..22f7a06ad 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerRegistrationExtensions.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerRegistrationExtensions.cs
@@ -1,11 +1,14 @@
-using Autofac;
-using Microsoft.Extensions.Logging;
-using ServiceFabric.Logging;
-using System;
-using System.Fabric;
-
-namespace Arcadia.Assistant.Logging
+namespace Arcadia.Assistant.Logging
{
+ using System;
+ using System.Fabric;
+
+ using Autofac;
+
+ using Microsoft.Extensions.Logging;
+
+ using ServiceFabric.Logging;
+
public static class LoggerRegistrationExtensions
{
public static void RegisterServiceLogging(
@@ -13,7 +16,9 @@ public static void RegisterServiceLogging(
LoggerSettings loggerSettings)
{
if (builder == null)
+ {
throw new ArgumentNullException(nameof(builder));
+ }
builder.Register((x, p) =>
{
@@ -45,7 +50,9 @@ public static void RegisterServiceLogging(
ServiceContext context)
{
if (builder == null)
+ {
throw new ArgumentNullException(nameof(builder));
+ }
var loggerFactoryBuilder = new LoggerFactoryBuilder(context);
var loggerFactory = loggerFactoryBuilder.CreateLoggerFactory(loggerSettings.ApplicationInsightsKey);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerSettings.cs
index 28bd7770c..bc9017516 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerSettings.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Logging/LoggerSettings.cs
@@ -20,11 +20,11 @@ public LoggerSettings(string appKey)
this.ApplicationInsightsKey = appKey;
}
+ public string ApplicationInsightsKey { get; set; } = string.Empty;
+
public static LoggerSettings FromInsightsKey(string applicationInsightsKey)
{
return new LoggerSettings(applicationInsightsKey);
}
-
- public string ApplicationInsightsKey { get; set; } = string.Empty;
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs
index 0ce34b730..e9d92afed 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs
@@ -29,7 +29,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs
index 53ed5c59c..5220f120f 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs
@@ -43,7 +43,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(Organization).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs
index f0c24dab8..a95b21bde 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(PendingActions).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs
index 22effe7f8..9f29ed86a 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs
@@ -39,7 +39,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(Permissions).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
index 33cc93761..b8b34e82d 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SF/ApplicationPackageRoot/ApplicationManifest.xml
@@ -30,14 +30,12 @@
-
-
+
-
@@ -78,13 +76,16 @@
+
-
+
-
@@ -134,7 +135,7 @@
-
+
@@ -149,7 +150,7 @@
-
-
-
-
+
@@ -251,7 +252,7 @@
-
-
+
@@ -302,7 +303,7 @@
-
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
index 986c111a4..1c2fc030c 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Arcadia.Assistant.Sharepoint.csproj
@@ -21,6 +21,7 @@
+
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
index cb090842b..9203a41fb 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
@@ -16,7 +16,10 @@ namespace Arcadia.Assistant.Sharepoint
using ExternalStorages.SharepointOnline;
using ExternalStorages.SharepointOnline.Contracts;
+ using Logging;
+
using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.Logging;
using Microsoft.ServiceFabric.Services.Remoting.Client;
using Models;
@@ -36,6 +39,7 @@ internal static class Program
///
private static void Main()
{
+ ILogger? logger = null;
try
{
var configurationPackage = FabricRuntime.GetActivationContext().GetConfigurationPackageObject("Config");
@@ -78,19 +82,19 @@ private static void Main()
builder.RegisterModule();
builder.RegisterModule();
builder.RegisterModule();
+ builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
builder.Populate(services);
- using (builder.Build())
- {
- ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Sharepoint).Name);
-
- // Prevents this host process from terminating so services keep running.
- Thread.Sleep(Timeout.Infinite);
- }
+ using var container = builder.Build();
+ logger = container.TryResolve(out ILogger log) ? log : null;
+ logger?.LogInformation($"Service type '{typeof(Sharepoint).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
+ // Prevents this host process from terminating so services keep running.
+ Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
{
ServiceEventSource.Current.ServiceHostInitializationFailed(e.ToString());
+ logger?.LogCritical(e, e.Message);
throw;
}
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs
index fdbc04123..3d5feb164 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs
@@ -45,7 +45,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(SickLeaves).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs
index f7a9b4832..0c040375e 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs
@@ -28,7 +28,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs
index 5820e0854..0a1142dec 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs
@@ -45,7 +45,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(Vacations).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs
index 1f7d28cbb..e0bdd492b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(VacationsCredit).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs
index 9d9483dda..ff0561e0c 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.Register(c => new WorkHoursCreditContext(c.Resolve>())).AsSelf();
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.TryResolve(out ILogger val) ? val : null;
logger?.LogInformation($"Service type '{typeof(WorkHoursCredit).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
From 5c87961d4b12500cf1ad306a30b8a0665312589a Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Tue, 11 Feb 2020 20:41:13 +0300
Subject: [PATCH 09/14] Add missed configuration
---
.../PackageRoot/Config/Settings.xml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
index 7fbecade1..be0876c59 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/PackageRoot/Config/Settings.xml
@@ -14,4 +14,7 @@
+
\ No newline at end of file
From 2a9789e6c351fa6162a9b78cf418bc371997ff58 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Wed, 12 Feb 2020 22:38:58 +0300
Subject: [PATCH 10/14] Fix PR comments
---
.../Program.cs | 2 +-
.../Program.cs | 2 +-
.../Arcadia.Assistant.Avatars/Program.cs | 2 +-
.../Arcadia.Assistant.Employees/Program.cs | 2 +-
.../Contracts/ISharepointFieldsMapper.cs | 2 -
.../Contracts/SharepointRequest.cs | 2 +-
.../SharepointListItemRequest.cs | 8 ++--
.../SharepointAuthTokenService.cs | 8 ----
.../SharepointFieldsMapper.cs | 2 +-
.../Arcadia.Assistant.Inbox/Program.cs | 2 +-
.../Arcadia.Assistant.MobileBuild/Program.cs | 2 +-
.../Arcadia.Assistant.Organization/Program.cs | 2 +-
.../Program.cs | 2 +-
.../Arcadia.Assistant.Permissions/Program.cs | 2 +-
.../Arcadia.Assistant.Sharepoint/Program.cs | 2 +-
.../SharepointItemSynchronization.cs | 4 +-
.../SharepointSynchronizator.cs | 43 +++++++++----------
.../Arcadia.Assistant.SickLeaves/Program.cs | 2 +-
.../Program.cs | 2 +-
.../Arcadia.Assistant.Vacations/Program.cs | 2 +-
.../Program.cs | 2 +-
.../Program.cs | 2 +-
.../WorkHoursCredit.cs | 3 +-
23 files changed, 45 insertions(+), 57 deletions(-)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs
index 0c876f5e0..19cd66d7a 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.AppCenterBuilds/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(AppCenterBuilds).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs
index d58e96e31..dde91aeb9 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars.Manager/Program.cs
@@ -48,7 +48,7 @@ private static void Main()
builder.RegisterStatelessService("Arcadia.Assistant.Avatars.ManagerType");
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(Manager).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs
index 4b08e0505..c3597f9a3 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Avatars/Program.cs
@@ -30,7 +30,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs
index 3b8e4085b..e121305dd 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Employees/Program.cs
@@ -39,7 +39,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(Employees).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
index b937bd8cb..f27d2ffb8 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/ISharepointFieldsMapper.cs
@@ -8,7 +8,5 @@
public interface ISharepointFieldsMapper
{
string GetSharepointField(Expression> property);
-
- string? GetSharepointField(string propertyName);
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
index 5e667e080..ba8703a4c 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
@@ -133,7 +133,7 @@ private void AddHeader(string name, string value)
private void RemoveHeader(string name)
{
- this.headersInternal.Where(x => x.Item1 == name).ToList().ForEach(x => this.headersInternal.Remove(x));
+ this.headersInternal.RemoveAll(x => x.Item1 == name);
}
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
index 64b5c6d80..7ec79a63f 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointApiModels/SharepointListItemRequest.cs
@@ -1,10 +1,9 @@
namespace Arcadia.Assistant.ExternalStorages.SharepointOnline.SharepointApiModels
{
- using System.Runtime.Serialization;
+ using System.Text.Json.Serialization;
using Abstractions;
- [DataContract]
public class SharepointListItemRequest : StorageItem
{
public SharepointListItemRequest(StorageItem storage, string? type = null)
@@ -23,10 +22,9 @@ public SharepointListItemRequest(StorageItem storage, string? type = null)
}
}
- [DataMember(Name = "__metadata")]
+ [JsonPropertyName("__metadata")]
public MetadataRequest Metadata { get; set; } = new MetadataRequest();
- [DataContract]
public class MetadataRequest
{
public MetadataRequest()
@@ -38,7 +36,7 @@ public MetadataRequest(string type)
this.Type = type;
}
- [DataMember(Name = "type")]
+ [JsonPropertyName("type")]
public string? Type { get; set; }
}
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
index 8e8d2e188..be630412b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointAuthTokenService.cs
@@ -7,7 +7,6 @@
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
- using System.Runtime.Serialization;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
@@ -183,31 +182,24 @@ private string GetFormattedPrincipal(string principalName, string? hostName, str
: string.Format(CultureInfo.InvariantCulture, "{0}/{1}@{2}", principalName, hostName, realm);
}
- [DataContract]
private class JsonMetadataDocument
{
[JsonPropertyName("endpoints")]
- [DataMember(Name = "endpoints")]
public IEnumerable? Endpoints { get; set; }
}
- [DataContract]
private class JsonEndpoint
{
[JsonPropertyName("location")]
- [DataMember(Name = "location")]
public string? Location { get; set; }
[JsonPropertyName("protocol")]
- [DataMember(Name = "protocol")]
public string? Protocol { get; set; }
}
- [DataContract]
private class OAuth2AccessTokenResponse
{
[JsonPropertyName("access_token")]
- [DataMember(Name = "access_token")]
public string? AccessToken { get; set; }
}
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
index a11f0aefb..306b3478a 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
@@ -53,7 +53,7 @@ public string GetSharepointField(Expression> property)
return fieldName;
}
- public string? GetSharepointField(string propertyName)
+ private string? GetSharepointField(string propertyName)
{
return this.fieldsByPropertyName.TryGetValue(propertyName, out var field) ? field : null;
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs
index 26fa18689..89bfef348 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Inbox/Program.cs
@@ -30,7 +30,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(Inbox).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
Thread.Sleep(Timeout.Infinite);
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs
index e9d92afed..ddc6239e7 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.MobileBuild/Program.cs
@@ -29,7 +29,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs
index 5220f120f..664066fa4 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Organization/Program.cs
@@ -43,7 +43,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(Organization).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs
index a95b21bde..690abdc26 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.PendingActions/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(PendingActions).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs
index 9f29ed86a..f91001a2f 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Permissions/Program.cs
@@ -39,7 +39,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(Permissions).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
index 9203a41fb..cd87dfc4e 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Program.cs
@@ -86,7 +86,7 @@ private static void Main()
builder.Populate(services);
using var container = builder.Build();
- logger = container.TryResolve(out ILogger log) ? log : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(Sharepoint).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
index 89e35f846..aa51b9d88 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointItemSynchronization.cs
@@ -28,7 +28,7 @@ protected SharepointItemSynchronization(IExternalStorage externalStorage, ILogge
#region public interface
- public async Task SynchronizeItem(string calendar, EmployeeMetadata[] departmentEmployes, Dictionary values, IEnumerable storageItemsList, CancellationToken cancellationToken)
+ public async Task SynchronizeItems(string calendar, EmployeeMetadata[] departmentEmployees, Dictionary values, IEnumerable storageItemsList, CancellationToken cancellationToken)
{
try
{
@@ -38,7 +38,7 @@ public async Task SynchronizeItem(string calendar, EmployeeMetadata[] department
// insert or update items
foreach (var workHourEventId in values.Keys)
{
- var employeeMetadata = departmentEmployes.Single(x => x.EmployeeId == this.GetItemEmployeeId(values[workHourEventId]));
+ var employeeMetadata = departmentEmployees.Single(x => x.EmployeeId == this.GetItemEmployeeId(values[workHourEventId]));
await this.UpsertItem(workHourEventId, calendar, values[workHourEventId], employeeMetadata, this.ExternalStorage, cancellationToken);
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
index d3293c4e8..b46deb01b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
@@ -34,7 +34,7 @@ public SharepointSynchronizator(ISickLeaves sickLeaves, IVacations vacations, IW
#endregion
- #region publci interface
+ #region public interface
public async Task Synchronize(IEmployees employees, IEnumerable departments, IExternalStorage storage, CancellationToken cancellationToken)
{
@@ -42,36 +42,35 @@ public async Task Synchronize(IEmployees employees, IEnumerable departme
var workoutsSync = new EmployeeWorkoutsSynchronization(storage, this.logger);
var sickLeavesSync = new EmployeeSickLeavesSynchronization(storage, this.logger);
- var sharepointCalendars = departments.Distinct().ToDictionary(x => x,
+ var sharepointItemsByCalendars = departments.Distinct().ToDictionary(x => x,
dId => this.GetSharepointCalendarsByDepartment(dId).ToDictionary(x => x,
async cal => await this.GetAllSharepointItemsForCalendar(storage, cal)));
foreach (var departmentId in departments)
{
- var departmentEmployes = await employees.FindEmployeesAsync(EmployeesQuery.Create().ForDepartment(departmentId), cancellationToken);
- var employeeIds = departmentEmployes.Select(x => x.EmployeeId).ToArray();
+ var departmentEmployees = await employees.FindEmployeesAsync(EmployeesQuery.Create().ForDepartment(departmentId), cancellationToken);
+ var employeeIds = departmentEmployees.Select(x => x.EmployeeId).ToArray();
- var employeeVacations = await this.vacations.GetCalendarEventsByEmployeeAsync(employeeIds, cancellationToken);
- var employeeVacationValues = employeeVacations.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
+ var employeeVacationsTask = this.vacations.GetCalendarEventsByEmployeeAsync(employeeIds, cancellationToken);
+ var employeeWorkoutsTask = this.workouts.GetCalendarEventsByEmployeeMapAsync(employeeIds, cancellationToken);
+ var employeeSickLeavesTask = this.sickLeaves.GetCalendarEventsByEmployeeMapAsync(employeeIds, cancellationToken);
+ await Task.WhenAll(employeeVacationsTask, employeeWorkoutsTask, employeeSickLeavesTask);
- var employeeWorkouts = await this.workouts.GetCalendarEventsByEmployeeMapAsync(employeeIds, cancellationToken);
- var employeeWorkoutsValues = employeeWorkouts.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
+ var employeeVacationValues = employeeVacationsTask.Result.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
+ var employeeWorkoutsValues = employeeWorkoutsTask.Result.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
+ var employeeSickLeavesValues = employeeSickLeavesTask.Result.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
- var employeeSickLeaves = await this.sickLeaves.GetCalendarEventsByEmployeeMapAsync(employeeIds, cancellationToken);
- var employeeSickLeavesValues = employeeSickLeaves.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
-
- foreach (var calendar in this.GetSharepointCalendarsByDepartment(departmentId))
+ foreach (var calendar in sharepointItemsByCalendars[departmentId].Keys)
{
- var storageItems = await sharepointCalendars[departmentId][calendar];
-
- // synchronize vacations for selected department
- await vacationsSync.SynchronizeItem(calendar, departmentEmployes, employeeVacationValues, storageItems, cancellationToken);
-
- // synchronize workouts for selected department
- await workoutsSync.SynchronizeItem(calendar, departmentEmployes, employeeWorkoutsValues, storageItems, cancellationToken);
-
- // synchronize vacations for selected department
- await sickLeavesSync.SynchronizeItem(calendar, departmentEmployes, employeeSickLeavesValues, storageItems, cancellationToken);
+ var storageItems = await sharepointItemsByCalendars[departmentId][calendar];
+
+ await Task.WhenAll(
+ // synchronize vacations for selected department
+ vacationsSync.SynchronizeItems(calendar, departmentEmployees, employeeVacationValues, storageItems, cancellationToken),
+ // synchronize workouts for selected department
+ workoutsSync.SynchronizeItems(calendar, departmentEmployees, employeeWorkoutsValues, storageItems, cancellationToken),
+ // synchronize vacations for selected department
+ sickLeavesSync.SynchronizeItems(calendar, departmentEmployees, employeeSickLeavesValues, storageItems, cancellationToken));
}
}
}
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs
index 3d5feb164..1e807fc8b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.SickLeaves/Program.cs
@@ -45,7 +45,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(SickLeaves).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs
index 0c040375e..8d12743f0 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.UserPreferences/Program.cs
@@ -28,7 +28,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
Thread.Sleep(Timeout.Infinite);
}
catch (Exception e)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs
index 0a1142dec..71498d1e8 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Vacations/Program.cs
@@ -45,7 +45,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(Vacations).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs
index e0bdd492b..41b38e9f2 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.VacationsCredit/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.RegisterServiceLogging(new LoggerSettings(configurationPackage.Settings.Sections["Logging"]));
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(VacationsCredit).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs
index ff0561e0c..9967642ad 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/Program.cs
@@ -46,7 +46,7 @@ private static void Main()
builder.Register(c => new WorkHoursCreditContext(c.Resolve>())).AsSelf();
using var container = builder.Build();
- logger = container.TryResolve(out ILogger val) ? val : null;
+ logger = container.ResolveOptional();
logger?.LogInformation($"Service type '{typeof(WorkHoursCredit).Name}' registered. Process: {Process.GetCurrentProcess().Id}.");
// Prevents this host process from terminating so services keep running.
Thread.Sleep(Timeout.Infinite);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
index 476e848d4..315d2f03b 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
@@ -86,7 +86,8 @@ public async Task> GetCalendarEventsBy
var events = await this.QueryCalendarEvents(ctx.Value.ChangeRequests, x => employeeIds.Any(id => id.Value == x.EmployeeId))
.ToArrayAsync(cancellationToken);
- return events.GroupBy(x=> x.EmployeeId).ToDictionary(x => x.Key, x=> x.ToArray());
+ return events.GroupBy(x=> x.EmployeeId)
+ .ToDictionary(x => x.Key, x=> x.ToArray());
}
public async Task GetCalendarEventAsync(EmployeeId employeeId, Guid eventId, CancellationToken cancellationToken)
From 768af1c5312f021795ad742dbcf1a1cb92d333d2 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Thu, 13 Feb 2020 09:46:26 +0300
Subject: [PATCH 11/14] Fixed PR comments
---
.../Sharepoint.cs | 2 +-
.../SharepointSynchronizator.cs | 81 ++++++++++---------
.../WorkHoursCredit.cs | 4 +-
3 files changed, 46 insertions(+), 41 deletions(-)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
index 55ca6e628..2e38d6d46 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
@@ -103,7 +103,7 @@ private async Task> GetDepartmentsList(CancellationToken can
{
if (this.departmentsCalendarsSettings.DepartmentsCalendars != null && this.departmentsCalendarsSettings.DepartmentsCalendars.Any())
{
- return this.departmentsCalendarsSettings.DepartmentsCalendars.Select(x => x.DepartmentId);
+ return this.departmentsCalendarsSettings.DepartmentsCalendars.Select(x => x.DepartmentId).Distinct();
}
return (await this.organizations.GetDepartmentsAsync(cancellationToken)).Select(x => x.DepartmentId.Value.ToString());
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
index b46deb01b..dbafcf75d 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
@@ -21,6 +21,16 @@
public sealed class SharepointSynchronizator
{
+ #region variables
+
+ private readonly ISickLeaves sickLeaves;
+ private readonly IVacations vacations;
+ private readonly IWorkHoursCredit workouts;
+ private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
+ private readonly ILogger? logger;
+
+ #endregion
+
#region ctor
public SharepointSynchronizator(ISickLeaves sickLeaves, IVacations vacations, IWorkHoursCredit workouts, ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings, ILogger? logger)
@@ -42,11 +52,8 @@ public async Task Synchronize(IEmployees employees, IEnumerable departme
var workoutsSync = new EmployeeWorkoutsSynchronization(storage, this.logger);
var sickLeavesSync = new EmployeeSickLeavesSynchronization(storage, this.logger);
- var sharepointItemsByCalendars = departments.Distinct().ToDictionary(x => x,
- dId => this.GetSharepointCalendarsByDepartment(dId).ToDictionary(x => x,
- async cal => await this.GetAllSharepointItemsForCalendar(storage, cal)));
-
- foreach (var departmentId in departments)
+ var storageItemsCache = new Dictionary>();
+ foreach (var departmentId in departments.Distinct())
{
var departmentEmployees = await employees.FindEmployeesAsync(EmployeesQuery.Create().ForDepartment(departmentId), cancellationToken);
var employeeIds = departmentEmployees.Select(x => x.EmployeeId).ToArray();
@@ -56,27 +63,50 @@ public async Task Synchronize(IEmployees employees, IEnumerable departme
var employeeSickLeavesTask = this.sickLeaves.GetCalendarEventsByEmployeeMapAsync(employeeIds, cancellationToken);
await Task.WhenAll(employeeVacationsTask, employeeWorkoutsTask, employeeSickLeavesTask);
- var employeeVacationValues = employeeVacationsTask.Result.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
- var employeeWorkoutsValues = employeeWorkoutsTask.Result.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
- var employeeSickLeavesValues = employeeSickLeavesTask.Result.Values.SelectMany(x => x).ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
+ var employeeVacationValues = employeeVacationsTask.Result.Values.SelectMany(x => x)
+ .ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.VacationId, CalendarEventTypes.Vacation), x => x);
+ var employeeWorkoutsValues = employeeWorkoutsTask.Result.Values.SelectMany(x => x)
+ .ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.ChangeId, CalendarEventTypes.Workout), x => x);
+ var employeeSickLeavesValues = employeeSickLeavesTask.Result.Values.SelectMany(x => x)
+ .ToDictionary(x => CspCalendarEventIdParser.GetCalendarEventIdFromCspId(x.SickLeaveId, CalendarEventTypes.Sickleave), x => x);
- foreach (var calendar in sharepointItemsByCalendars[departmentId].Keys)
+ foreach (var calendar in this.GetSharepointCalendarsByDepartment(departmentId))
{
- var storageItems = await sharepointItemsByCalendars[departmentId][calendar];
+ if (!storageItemsCache.ContainsKey(calendar))
+ {
+ storageItemsCache.Add(calendar, await this.GetAllSharepointItemsForCalendar(storage, calendar));
+ }
await Task.WhenAll(
// synchronize vacations for selected department
- vacationsSync.SynchronizeItems(calendar, departmentEmployees, employeeVacationValues, storageItems, cancellationToken),
+ vacationsSync.SynchronizeItems(calendar, departmentEmployees, employeeVacationValues, storageItemsCache[calendar], cancellationToken),
// synchronize workouts for selected department
- workoutsSync.SynchronizeItems(calendar, departmentEmployees, employeeWorkoutsValues, storageItems, cancellationToken),
+ workoutsSync.SynchronizeItems(calendar, departmentEmployees, employeeWorkoutsValues, storageItemsCache[calendar], cancellationToken),
// synchronize vacations for selected department
- sickLeavesSync.SynchronizeItems(calendar, departmentEmployees, employeeSickLeavesValues, storageItems, cancellationToken));
+ sickLeavesSync.SynchronizeItems(calendar, departmentEmployees, employeeSickLeavesValues, storageItemsCache[calendar], cancellationToken));
}
}
}
#endregion
+ #region private
+
+ private async Task> GetAllSharepointItemsForCalendar(IExternalStorage externalStorage, string calendar)
+ {
+ return await externalStorage.GetItems(calendar);
+ }
+
+ private IEnumerable GetSharepointCalendarsByDepartment(string departmentId)
+ {
+ return this.departmentsCalendarsSettings.DepartmentsCalendars
+ .Where(x => x.DepartmentId == departmentId)
+ .Select(x => x.Calendar)
+ .Distinct();
+ }
+
+ #endregion
+
#region internal class
private class EmployeeVacationsSynchronization : SharepointItemSynchronization
@@ -141,30 +171,5 @@ protected override EmployeeId GetItemEmployeeId(SickLeaveDescription item)
#endregion
- #region variables
-
- private readonly ISickLeaves sickLeaves;
- private readonly IVacations vacations;
- private readonly IWorkHoursCredit workouts;
- private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
- private readonly ILogger? logger;
-
- #endregion
-
- #region private
-
- private async Task> GetAllSharepointItemsForCalendar(IExternalStorage externalStorage, string calendar)
- {
- return await externalStorage.GetItems(calendar);
- }
-
- private IEnumerable GetSharepointCalendarsByDepartment(string departmentId)
- {
- return this.departmentsCalendarsSettings.DepartmentsCalendars
- .Where(x => x.DepartmentId == departmentId)
- .Select(x => x.Calendar);
- }
-
- #endregion
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
index 315d2f03b..0faa177aa 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.WorkHoursCredit/WorkHoursCredit.cs
@@ -86,8 +86,8 @@ public async Task> GetCalendarEventsBy
var events = await this.QueryCalendarEvents(ctx.Value.ChangeRequests, x => employeeIds.Any(id => id.Value == x.EmployeeId))
.ToArrayAsync(cancellationToken);
- return events.GroupBy(x=> x.EmployeeId)
- .ToDictionary(x => x.Key, x=> x.ToArray());
+ return events.GroupBy(x => x.EmployeeId)
+ .ToDictionary(x => x.Key, x => x.ToArray());
}
public async Task GetCalendarEventAsync(EmployeeId employeeId, Guid eventId, CancellationToken cancellationToken)
From 6d12fe239093f474a833530d92bc2e2ed3e7dbe5 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Thu, 13 Feb 2020 12:15:56 +0300
Subject: [PATCH 12/14] Fix by PR comment
---
.../SharepointFieldsMapper.cs | 10 +++++-----
.../SharepointDepartmentsCalendarsSettings.cs | 13 ++-----------
.../Models/SharepointSynchronizationSettings.cs | 4 +++-
3 files changed, 10 insertions(+), 17 deletions(-)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
index 306b3478a..7f6976dd1 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointFieldsMapper.cs
@@ -53,11 +53,6 @@ public string GetSharepointField(Expression> property)
return fieldName;
}
- private string? GetSharepointField(string propertyName)
- {
- return this.fieldsByPropertyName.TryGetValue(propertyName, out var field) ? field : null;
- }
-
public static SharepointFieldMapping CreateMapping(Expression> property, string fieldName)
{
return new SharepointFieldMapping(property, fieldName);
@@ -68,6 +63,11 @@ private string GetPropertyName(Expression> property)
return new PropertyNameParser().GetName(property);
}
+ private string? GetSharepointField(string propertyName)
+ {
+ return this.fieldsByPropertyName.TryGetValue(propertyName, out var field) ? field : null;
+ }
+
public struct SharepointFieldMapping
{
public SharepointFieldMapping(Expression> property, string field)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentsCalendarsSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentsCalendarsSettings.cs
index ee53f0169..346885edf 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentsCalendarsSettings.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointDepartmentsCalendarsSettings.cs
@@ -1,6 +1,5 @@
namespace Arcadia.Assistant.Sharepoint.Models
{
- using System;
using System.Collections.Generic;
using System.Fabric.Description;
using System.Text.Json;
@@ -9,16 +8,8 @@ public class SharepointDepartmentsCalendarsSettings : ISharepointDepartmentsCale
{
public SharepointDepartmentsCalendarsSettings(ConfigurationSection configurationSection)
{
- try
- {
- var val = configurationSection.Parameters["Value"].Value;
- this.DepartmentsCalendars = JsonSerializer.Deserialize>(val);
- }
- catch (Exception e)
- {
- Console.WriteLine(e);
- throw;
- }
+ var val = configurationSection.Parameters["Value"].Value;
+ this.DepartmentsCalendars = JsonSerializer.Deserialize>(val);
}
public IEnumerable DepartmentsCalendars { get; set; }
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointSynchronizationSettings.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointSynchronizationSettings.cs
index f3f022a3a..624bae972 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointSynchronizationSettings.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Models/SharepointSynchronizationSettings.cs
@@ -8,7 +8,9 @@ public class SharepointSynchronizationSettings : ISharepointSynchronizationSetti
public SharepointSynchronizationSettings(ConfigurationSection configurationSection)
{
- this.SynchronizationIntervalMinutes = int.TryParse(configurationSection.Parameters["SynchronizationIntervalMinutes"].Value, out var interval) ? interval : this.DefaultSynchronizationIntervalMinutes;
+ this.SynchronizationIntervalMinutes = int.TryParse(configurationSection.Parameters["SynchronizationIntervalMinutes"].Value, out var interval)
+ ? interval
+ : this.DefaultSynchronizationIntervalMinutes;
}
public int SynchronizationIntervalMinutes { get; }
From b0e4a9c3854deba4c73b57c634cc702959c9fc76 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Thu, 13 Feb 2020 16:28:04 +0300
Subject: [PATCH 13/14] Fixed PR comments
---
.../Contracts/SharepointRequest.cs | 41 +++++++++----------
.../SharepointStorage.cs | 3 +-
.../StorageItemJsonConvertor.cs | 15 +++----
.../Sharepoint.cs | 8 ++--
.../SharepointSynchronizator.cs | 10 ++---
5 files changed, 36 insertions(+), 41 deletions(-)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
index ba8703a4c..0565207f4 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/Contracts/SharepointRequest.cs
@@ -16,7 +16,12 @@ public class SharepointRequest
private const string IfMatchHeaderName = "IF-MATCH";
private const string XHttpMethodHeaderName = "X-HTTP-Method";
- private readonly List> headersInternal = new List>();
+ /**** According rfc7230 3.2.2 p.2
+ * A sender must not generate multiple header fields with the same field name in a message unless either the entire field
+ * value for that header field is defined as a comma-separated list [i.e., #(values)] or the header field is a well-known
+ * exception (as noted below).
+ */
+ private readonly Dictionary> headersInternal = new Dictionary>();
private SharepointRequest(HttpMethod httpMethod, string url)
{
@@ -30,25 +35,14 @@ private SharepointRequest(HttpMethod httpMethod, string url)
public HttpContent? Content { get; private set; }
- public IReadOnlyList> Headers => this.headersInternal;
-
public static SharepointRequest Create(HttpMethod httpMethod, string url)
{
return new SharepointRequest(httpMethod, url);
}
- public SharepointRequest WithAcceptHeader(string value, bool update = true)
+ public SharepointRequest WithAcceptHeader(string value)
{
- if (update)
- {
- this.RemoveHeader(AcceptHeaderName);
- }
-
- if (update || !this.headersInternal.Any(x => x.Item1 == AcceptHeaderName))
- {
- this.AddHeader(AcceptHeaderName, value);
- }
-
+ this.AddHeader(AcceptHeaderName, value);
return this;
}
@@ -95,8 +89,9 @@ public HttpRequestMessage GetHttpRequest()
httpRequest.Content = this.Content;
}
- foreach (var (headerName, headerValue) in this.headersInternal)
+ foreach (var (headerName, headerValues) in this.headersInternal)
{
+ var headerValue = string.Join(", ", headerValues);
switch (headerName)
{
case AcceptHeaderName:
@@ -128,12 +123,16 @@ public HttpRequestMessage GetHttpRequest()
private void AddHeader(string name, string value)
{
- this.headersInternal.Add(Tuple.Create(name, value));
- }
-
- private void RemoveHeader(string name)
- {
- this.headersInternal.RemoveAll(x => x.Item1 == name);
+ if (!this.headersInternal.TryGetValue(name, out var headerValues))
+ {
+ this.headersInternal.Add(name, new List()
+ {
+ value
+ });
+ } else if (!headerValues.Contains(value))
+ {
+ this.headersInternal[name].Add(value);
+ }
}
}
}
\ No newline at end of file
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
index 251bb0385..1a11c4bea 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
@@ -65,8 +65,7 @@ public async Task UpdateItem(string list, StorageItem item, CancellationToken ca
var existingListItem = listItems.First();
- var updateItemUrl = this.GetListItemsUrl(list, false);
- updateItemUrl += $"({existingListItem.Id})";
+ var updateItemUrl = $"{this.GetListItemsUrl(list, false)}({existingListItem.Id})";
var listItemType = await this.GetListItemType(list, cancellationToken);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs
index 33389cf3c..202441515 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/StorageItemJsonConvertor.cs
@@ -76,8 +76,8 @@ public string StorageItemToRequestJson(StorageItem item, string? listItemType)
Id = this.GetJsonInt(item, this.FieldsMapper.GetSharepointField(x => x.Id)),
Title = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.Title)),
Description = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.Description)),
- StartDate = this.GetJsonDate(item, this.FieldsMapper.GetSharepointField(x => x.StartDate), allDayEvent, DateTime.MinValue),
- EndDate = this.GetJsonDate(item, this.FieldsMapper.GetSharepointField(x => x.EndDate), allDayEvent, DateTime.UtcNow),
+ StartDate = this.GetJsonDate(item, this.FieldsMapper.GetSharepointField(x => x.StartDate), DateTime.MinValue, allDayEvent),
+ EndDate = this.GetJsonDate(item, this.FieldsMapper.GetSharepointField(x => x.EndDate), DateTime.UtcNow, allDayEvent),
Category = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.Category)),
AllDayEvent = allDayEvent,
CalendarEventId = this.GetJsonString(item, this.FieldsMapper.GetSharepointField(x => x.CalendarEventId))
@@ -103,15 +103,10 @@ private bool GetJsonBool(JsonElement item, string propName, bool defaultValue =
return item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null ? prop.GetBoolean() : defaultValue;
}
- private DateTime GetJsonDate(JsonElement item, string propName, bool dateOnly, DateTime defaultValue)
+ private DateTime GetJsonDate(JsonElement item, string propName, DateTime defaultValue, bool dateOnly = false)
{
- var date = item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null && prop.TryGetDateTime(out var val) ? val : defaultValue;
- return dateOnly ? new DateTime(date.Year, date.Month, date.Day) : date;
- }
-
- private DateTime GetJsonDate(JsonElement item, string propName, DateTime defaultValue)
- {
- return item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null && prop.TryGetDateTime(out var val) ? val : defaultValue;
+ var date = item.TryGetProperty(propName, out var prop) && prop.ValueKind != JsonValueKind.Null && prop.TryGetDateTime(out var val) ? val : defaultValue; ;
+ return dateOnly ? date.Date : date;
}
#endregion
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
index 2e38d6d46..ceee1e392 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
@@ -31,7 +31,7 @@ public class Sharepoint : StatelessService
private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
private readonly IEmployees employees;
private readonly Func externalStorageProvider;
- private readonly ILogger? logger = null; // TO DO - initialize variable
+ private readonly ILogger? logger;
private readonly IOrganization organizations;
private readonly ISharepointSynchronizationSettings serviceSettings;
private readonly ISickLeaves sickLeaves;
@@ -47,7 +47,8 @@ public Sharepoint(
IOrganization organizations,
Func externalStorageProvider,
ISharepointSynchronizationSettings serviceSettings,
- ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings)
+ ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings,
+ ILogger logger)
: base(context)
{
this.externalStorageProvider = externalStorageProvider;
@@ -58,6 +59,7 @@ public Sharepoint(
this.organizations = organizations;
this.serviceSettings = serviceSettings;
this.departmentsCalendarsSettings = departmentsCalendarsSettings;
+ this.logger = logger;
}
///
@@ -92,7 +94,7 @@ protected override async Task RunAsync(CancellationToken cancellationToken)
}
catch (Exception e)
{
- this.logger?.LogError(e, "Sharepoint items synchronization fail");
+ this.logger.LogError(e, "Sharepoint items synchronization fail");
}
await Task.Delay(TimeSpan.FromMinutes(this.serviceSettings.SynchronizationIntervalMinutes), cancellationToken);
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
index dbafcf75d..202428b98 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/SharepointSynchronizator.cs
@@ -27,13 +27,13 @@ public sealed class SharepointSynchronizator
private readonly IVacations vacations;
private readonly IWorkHoursCredit workouts;
private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
- private readonly ILogger? logger;
+ private readonly ILogger logger;
#endregion
#region ctor
- public SharepointSynchronizator(ISickLeaves sickLeaves, IVacations vacations, IWorkHoursCredit workouts, ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings, ILogger? logger)
+ public SharepointSynchronizator(ISickLeaves sickLeaves, IVacations vacations, IWorkHoursCredit workouts, ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings, ILogger logger)
{
this.sickLeaves = sickLeaves;
this.vacations = vacations;
@@ -111,7 +111,7 @@ private IEnumerable GetSharepointCalendarsByDepartment(string department
private class EmployeeVacationsSynchronization : SharepointItemSynchronization
{
- public EmployeeVacationsSynchronization(IExternalStorage externalStorage, ILogger? logger = null)
+ public EmployeeVacationsSynchronization(IExternalStorage externalStorage, ILogger logger)
: base(externalStorage, logger)
{
}
@@ -131,7 +131,7 @@ protected override EmployeeId GetItemEmployeeId(VacationDescription item)
private class EmployeeWorkoutsSynchronization : SharepointItemSynchronization
{
- public EmployeeWorkoutsSynchronization(IExternalStorage externalStorage, ILogger? logger = null)
+ public EmployeeWorkoutsSynchronization(IExternalStorage externalStorage, ILogger logger)
: base(externalStorage, logger)
{
}
@@ -151,7 +151,7 @@ protected override EmployeeId GetItemEmployeeId(WorkHoursChange item)
private class EmployeeSickLeavesSynchronization : SharepointItemSynchronization
{
- public EmployeeSickLeavesSynchronization(IExternalStorage externalStorage, ILogger? logger = null)
+ public EmployeeSickLeavesSynchronization(IExternalStorage externalStorage, ILogger logger)
: base(externalStorage, logger)
{
}
From d2ba4c7cfa32ff813f5d980998140447ec154291 Mon Sep 17 00:00:00 2001
From: "sergey.shilov" <>
Date: Thu, 13 Feb 2020 21:30:28 +0300
Subject: [PATCH 14/14] Fix PR comments
---
.../SharepointStorage.cs | 3 +--
.../Arcadia.Assistant.Sharepoint/Sharepoint.cs | 2 +-
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
index 1a11c4bea..533490ca8 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.ExternalStorages.SharepointOnline/SharepointStorage.cs
@@ -90,8 +90,7 @@ public async Task DeleteItem(string list, string itemId, CancellationToken cance
var existingListItem = listItems.First();
- var deleteItemUrl = this.GetListItemsUrl(list, false);
- deleteItemUrl += $"({existingListItem.Id})";
+ var deleteItemUrl = $"{this.GetListItemsUrl(list, false)}({existingListItem.Id})";
var request = SharepointRequest
.Create(HttpMethod.Post, deleteItemUrl)
diff --git a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
index ceee1e392..af256d7ea 100644
--- a/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
+++ b/server2/Arcadia.Assistant/Arcadia.Assistant.Sharepoint/Sharepoint.cs
@@ -31,7 +31,7 @@ public class Sharepoint : StatelessService
private readonly ISharepointDepartmentsCalendarsSettings departmentsCalendarsSettings;
private readonly IEmployees employees;
private readonly Func externalStorageProvider;
- private readonly ILogger? logger;
+ private readonly ILogger logger;
private readonly IOrganization organizations;
private readonly ISharepointSynchronizationSettings serviceSettings;
private readonly ISickLeaves sickLeaves;