From effd90b9e8dd75571332c97dcad85dc6b4162c6f Mon Sep 17 00:00:00 2001 From: RzR Date: Fri, 22 Dec 2023 18:23:32 +0200 Subject: [PATCH] Init commit --- .dockerignore | 25 ++ .gitattributes | 63 ++++ .gitignore | 273 ++++++++++++++ LICENSE | 21 ++ README.md | 30 ++ assets/PackageIcon.png | Bin 0 -> 87226 bytes build/pack-repo.ps1 | 270 ++++++++++++++ build/pack.ps1 | 21 ++ docs/CHANGELOG.md | 0 docs/branch-guide.md | 14 + docs/usage.md | 108 ++++++ src/MLogHelperDotNet/.editorconfig | 250 +++++++++++++ .../IMLogEndpointClientService.cs | 90 +++++ .../Configurations/CertificateConverter.cs | 71 ++++ .../Configurations/CertificateLoader.cs | 229 ++++++++++++ .../Options/MLogPostConfigureOptions.cs | 114 ++++++ .../MLogPostConfigureOptionsNetFramework.cs | 134 +++++++ src/MLogHelperDotNet/DependencyInjection.cs | 96 +++++ src/MLogHelperDotNet/Enums/EndpointType.cs | 36 ++ src/MLogHelperDotNet/Enums/MLogLevelType.cs | 46 +++ .../Extensions/HttpClientExtension.cs | 169 +++++++++ .../Extensions/StringExtensions.cs | 42 +++ .../Extensions/TExtensions.cs | 50 +++ .../ConfSection/MLogConfigurationSection.cs | 96 +++++ src/MLogHelperDotNet/Helpers/JsonHelper.cs | 105 ++++++ .../MLogConfigurationSectionNetFramework.cs | 102 +++++ .../MLogHelperDotNet.DotSettings | 3 + src/MLogHelperDotNet/MLogHelperDotNet.csproj | 99 +++++ src/MLogHelperDotNet/Models/MLogEventDto.cs | 349 ++++++++++++++++++ .../Models/RemoteMLogOptions.cs | 94 +++++ .../Services/MLogRestClientService.cs | 198 ++++++++++ .../Services/MLogSoapClientService.cs | 73 ++++ src/RzR.Shared.eGovMD.sln | 57 +++ src/RzR.Shared.eGovMD.sln.DotSettings | 2 + src/shared/GeneralAssemblyInfo.cs | 46 +++ src/tests/TestConsoleNet45/App.config | 24 ++ src/tests/TestConsoleNet45/App.xsd | 39 ++ src/tests/TestConsoleNet45/Program.cs | 34 ++ .../Properties/AssemblyInfo.cs | 36 ++ .../TestConsoleNet45/TestConsoleNet45.csproj | 80 ++++ .../Controllers/WeatherForecastController.cs | 64 ++++ src/tests/WebApiNet5/Program.cs | 20 + .../WebApiNet5/Properties/launchSettings.json | 31 ++ src/tests/WebApiNet5/Startup.cs | 58 +++ src/tests/WebApiNet5/WebApiNet5.csproj | 16 + .../WebApiNet5/appsettings.Development.json | 9 + src/tests/WebApiNet5/appsettings.json | 19 + 47 files changed, 3806 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 assets/PackageIcon.png create mode 100644 build/pack-repo.ps1 create mode 100644 build/pack.ps1 create mode 100644 docs/CHANGELOG.md create mode 100644 docs/branch-guide.md create mode 100644 docs/usage.md create mode 100644 src/MLogHelperDotNet/.editorconfig create mode 100644 src/MLogHelperDotNet/Abstractions/IMLogEndpointClientService.cs create mode 100644 src/MLogHelperDotNet/Configurations/CertificateConverter.cs create mode 100644 src/MLogHelperDotNet/Configurations/CertificateLoader.cs create mode 100644 src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptions.cs create mode 100644 src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptionsNetFramework.cs create mode 100644 src/MLogHelperDotNet/DependencyInjection.cs create mode 100644 src/MLogHelperDotNet/Enums/EndpointType.cs create mode 100644 src/MLogHelperDotNet/Enums/MLogLevelType.cs create mode 100644 src/MLogHelperDotNet/Extensions/HttpClientExtension.cs create mode 100644 src/MLogHelperDotNet/Extensions/StringExtensions.cs create mode 100644 src/MLogHelperDotNet/Extensions/TExtensions.cs create mode 100644 src/MLogHelperDotNet/Helpers/ConfSection/MLogConfigurationSection.cs create mode 100644 src/MLogHelperDotNet/Helpers/JsonHelper.cs create mode 100644 src/MLogHelperDotNet/Helpers/MLogConfigurationSectionNetFramework.cs create mode 100644 src/MLogHelperDotNet/MLogHelperDotNet.DotSettings create mode 100644 src/MLogHelperDotNet/MLogHelperDotNet.csproj create mode 100644 src/MLogHelperDotNet/Models/MLogEventDto.cs create mode 100644 src/MLogHelperDotNet/Models/RemoteMLogOptions.cs create mode 100644 src/MLogHelperDotNet/Services/MLogRestClientService.cs create mode 100644 src/MLogHelperDotNet/Services/MLogSoapClientService.cs create mode 100644 src/RzR.Shared.eGovMD.sln create mode 100644 src/RzR.Shared.eGovMD.sln.DotSettings create mode 100644 src/shared/GeneralAssemblyInfo.cs create mode 100644 src/tests/TestConsoleNet45/App.config create mode 100644 src/tests/TestConsoleNet45/App.xsd create mode 100644 src/tests/TestConsoleNet45/Program.cs create mode 100644 src/tests/TestConsoleNet45/Properties/AssemblyInfo.cs create mode 100644 src/tests/TestConsoleNet45/TestConsoleNet45.csproj create mode 100644 src/tests/WebApiNet5/Controllers/WeatherForecastController.cs create mode 100644 src/tests/WebApiNet5/Program.cs create mode 100644 src/tests/WebApiNet5/Properties/launchSettings.json create mode 100644 src/tests/WebApiNet5/Startup.cs create mode 100644 src/tests/WebApiNet5/WebApiNet5.csproj create mode 100644 src/tests/WebApiNet5/appsettings.Development.json create mode 100644 src/tests/WebApiNet5/appsettings.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..3729ff0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2aa9fef --- /dev/null +++ b/.gitignore @@ -0,0 +1,273 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +#*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ +.eslintrc.js +package-lock.json +package.json + +# Bower Libraries +wwwroot/lib/ + +# Sonarqube +.sonarqube + +# Coverlet +Report/ +coverage.* + +# Certificates +*.pfx +*.cer \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6dfea40 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022-2023 RzR + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2529bd0 --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +> **Note** This repository is developed using .net framework 4.5, .netstandard2.0, .netstandard2.1, net5.0, netcoreapp3.1. + +[![NuGet Version](https://img.shields.io/nuget/v/MLogHelperDotNet.svg?style=flat&logo=nuget)](https://www.nuget.org/packages/MLogHelperDotNet/) +[![Nuget Downloads](https://img.shields.io/nuget/dt/MLogHelperDotNet.svg?style=flat&logo=nuget)](https://www.nuget.org/packages/MLogHelperDotNet) + + +One important reason for developing this repository is to quickly implement the governmental logging service provided by [e-governance agency](https://egov.md/), named `MLog`, available in the Republic of Moldova.
+ +The current repository appears as a result of several implementations in projects from scratch, losing a lot of time and desire for a more easy way of implementation in new projects. +This repository is a wrapper for the currently available service. Using a few configuration parameters from the application settings file `appsettings.json`, `app.config`, or `web.config` you may implement them very easily into your own application.
+Using the wrapper you will no longer be forced to install the application certificate on the current machine/server. +
+ +Available configuration settings are: +* `ServiceClientAddress` -> `MLog` service URL; +* `ServiceCertificatePath` -> Service/application certificate for `MLog` (file with *.pfx at the end); +* `ServiceEventRegisterPath` -> Service/application registration log path; +* `ServiceEventGetPath` -> Service/application query log path; +* `ServiceTimeoutInMinute` -> Service/application request execution timeout. + +For more information about that, follow the info from using doc. + +**In case you wish to use it in your project, u can install the package from nuget.org** or specify what version you want: + +> `Install-Package MLogHelperDotNet -Version x.x.x.x` + +## Content +1. [USING](docs/usage.md) +1. [CHANGELOG](docs/CHANGELOG.md) +1. [BRANCH-GUIDE](docs/branch-guide.md) \ No newline at end of file diff --git a/assets/PackageIcon.png b/assets/PackageIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..6b06f82863ed7d5006f6d7d4649c0c935a856549 GIT binary patch literal 87226 zcmdRVgL7Q(`*zgEYHZuK-PpFBG}zd-8ncaUJB^(*wz;wSZa?4o{SoiZ?C#7nbLO0L z^SZC+dLoq+q!8h7;lRMa5M`vrRlvX?&cVRI^mM(vx`Hrd<<*Ie6 z;-1!B+ci1yTyvT2xNK~RnGomq=$2z^X{nIKqndl0d*p3^4i_p~phW%u*`J@g)oB7P zdeq(|ST0W4-lB!eqvh$KI(3{-dv%4TdI2}$Y$F<3)lVZ$YV_e8aDD*->==oxy{LwF z^Sy>I?nVeGh!bDCzkVMZLaf%P=@z}7R}RuI;1>AztCWq3uvd2cwJ|3^zQwv#v`P&r zLPyKbxN{s>D-JFMu?21$c;7o!?;LEqFbldRKE*Od)O<1;#j?HNBgl}0EHe@A1xFnc z$TLnuP}}ZW-pdc1C-SiFX$}j!v&T@CV8!6Kfu#4DbpMRLLj<}UZqQjGd|cqn1zN{S z8laSi7s57He8MU3b0j`W0}BKFBY7*U4n0;Sf4Q_Zp5R!6n5?HKU*JX&}~|3%3Yg{iWXVSy4p)4EgW zWBH!|RX{p0BCN%hQ9#+{ge+hS!oRn1(N!*&n{39?;=fz@iIk>bhlf>b3!hU8 zu4TSWaqU70iM~vQ4*NK+JO#StnnPcS(`&WhoiJ-4whV~gYoneM5t=?N#|OEwLi|9j zmGoGPr5>rHRhBGJqG5p>Jc;}ssZ5Pd_0vug3BusV-YH!R9^^h)nJ^-;9i7|ctC_t* zc%3af#jlkULS4c-q=`Fq)<5EQP`4*l)RHq!UqEw*~td9*H|qf z#CJUTMJ1~S-L(z>vzesEmhAWt6639Q&VP9&#P*-Hp-0UBesR$IOx(o=i2f-Fkaljv z#)jYyoDLw^TyUF5GF9tGoN&eFiyaL-7Fi75Z~fHi`1F=%-*JqGnwqQ1EWNr+m0@K| zrkYAu=X7@pohJP+Z{K2+{zYuUbg35A+c08U#OHkk%3Z4yU_OTo@?d*9P#9NwnK}$^E^S z4!y^z9{Zk-iXT!OuO?(8EEglb!muO9%XL}(=7af}wknO_J7CnNBiwnj=9hH1bEvj& zHY1@<+gfi=y3musWSQkJ)Ze;I7x_O&`^tXp3Mz8wtNhE5X+(-;e~8bI=`#}BC{9}BDpC!8F>meHae%hA^F*+rY2Ozv~k8ot2(XBpdkE(larGJ(1*iz36sTR zrmcgGb#5^=)y^4kvTm%ds;a+`16W^mD(3eeoQ}q4l%99)fr=5HpBV_ALHj+E{L86D zesF#5A)`BL}lA*I8(2dtAlEHEIqsG@Y zZXR-y?7zafXI#*n-6kccWkloSg^}H0lpWG^(fa}LSEyI+pe%z<()oIISJex@+srp& zqGep}6{@;H7gS9C>MUd?Xkx&{x}N1)p%G}|DyL5|Z+4g#OQ5pf01>)t84%^BMy;Tz zpjNrZq*J@)eF^D4X)?Xq?PAzrKECu&UA_)lyNKPep&Jj%#3iPO`b9>}i=VVdLW22= zl$aDBKhOy4K@WaeoMd*lg4}ga7GvGP#pZ{ouYlTu@U!2tJ`&$!?^M?z@RS#&4LfdI zT7Z<-)-#{X&iJySeKIm0Q8za;N$JNhK?|*a^=^8C`aKsI>PiG5P7onpTy9nf9~&V+ zB#LD5y;f?7wwvml%6tzx)yo2>cish@>}3|m`ggM|wz{R%Ou|VohMx)JE`enEIb?U5 z3@7P;Zx#PaDIFf4_=-8Y5d6PHRZvVZ5>nv5zq@70;=IIt0d`6Za8~LHc2-+hic04v zw@OP#gazS`NC(y`p%9pcFk{tpm8Qb?F;JGwqi^rNs_-w8=X8Q|I<-x%Z4QsSsIF5% zbuYkyo4_%rp+<~(+Y|i#+ZU&=)QI}sz3-t*NgJ}yII?N9?74xr(ec(?L8Ct{Ug)7^ zm+fLuGh3w|$CJTo+N1omzv99n&p~Zr12`YU)k$c^wUyls6ZpY$#aP>J_a;mdRYX9U zua+z(8;Vi`%uy`!6Df5b1F+|Lk8IfR~eLNxc*rSDHmnhHWp__6VZbD)yDBB-4Z}YdaxZEqLQ+C zl&^B&v_g1u8}l3fvL?7Kr87OQa=xo z#;?wd?fW10?sJ)d*?sC34aFiZXTXkh>p{^+5pbyr{k4rA&#; zN%yv`SSq{V|4M$gu)@>*X*5$e^>{hgN_E#qSQndxpFR-=j=ujEByzj21wT^f!)nN% zFVULFPjAr~Ju5BV3&LFmaY6Lhi$tqOS-CDGVHLLW@KGY;(r`4(x*7|EJto4|(kOqq zL{*cuF|gP)IYegF_`Fm}ak^BTm%b{`HfCHco3JKr40S>;mWWuPu(M~pZsQbs&4hJ6 z$V9l;Ra!ceY!3vHj2M3yb|PB5gTbc_eCR~To+$Bq%;fvKou)Jxt>8PridC1$j?q_D6!fBTD$7NN}(#hJ`oatfdd0dYbjcceC z6`H5yxw&s4=K0*qM9u+oJw- zQ0^1VTvGO7f~#W^znQyFNP|P_IAK6W2#}<+%d|DUJw@@-OCL;lBb+ca7_W>aVzH5~ zVwq91)Ad*)8!&Q4Eu;u=A~~-}>i?vEkm1MLX^4kmW8f^}+s?Y&ULz_fCkd$Ryx1}T zOtzL>yp9tPJy-T3BYyxLn|l*byq7LEf(`xN?}K92^>l;hGX%Xa*6(5_~-^!wM;!Fc06|7mg>#G6fDxSpJ=>6Z5f9p`KFvUm2N_?h|bEPkSt$ z)aFQK^bZus5`Vz`t1hC>17)M6OljkSVi$U)Tafn}=%HeK#jN9eMA6_3_8uzx#0~ZLvy!i;Jo(QKYLtpRkZZOrAk)Arn$O z{=NxqF)ab*$rbZ_Ju{lZ19DGN)CueyD`?Haz;>&S=BLQ>?@^gxCjaH3a^Jl9j%Wq> z*tWP*>2!AaO0uk(2q1z`OWZL^X=&omhu`8pj?wqf&?4fc*7Mq$2o3%LUu=m5S!@R` zFWRp+g+8to(FQSdy>3Pgm=v--;t8>_cWta-HkCCnF;1Okv{cc4FD>!mw_nt9utRYy zE}&BDeuwMuK zx41tQo8K;7(|P_iFAu^;`Eb~uC(I(P{qe_U+c4axEh*e9Ov#W6TeJjxW>wSpe$pA- z`gTg$q8%x{n%Ps-ebq|Yx!H-q3ebqS`~xWGs3#~Ha1LqguDY525re*rJ1k|8mIMn; zKb165Ll^&ij0q@PBvwXN30*)kB!to#;y&Ep(X34ou7M3@iB#M2=^ZK%m8d6ggsS{&jiTf6H}me!6QSnDc=s_Nw^}(@e)MBp*4x z+~oa%AY}Ik7$%HswQN6bGYD!ku6T=lZNxffWP-?*Y0jrMScC8rr_NO*_KplS~86q?2JS; z<@dN5G*SV{O{CQKZOFo?*@OMm>}yKovJLck-A4-Ch5wCXQOW(V80VuW&-0K#T*M+- z;x+O`zkTWHhVz)VQYs zh(8@T9k(JUZ7BHNne535x-lVuq`Cx^7u39#W~@7Bj*$p)`E|6RKhGBDOB%A?e|E14 zO1HgwBYhCg_Z|dwVdP(iBR$VBLc3Q*4%syQ>3O*5NIhff$@UH;{6U`E0s|NurTFCh zK3M#^5-MC45t7-_tXZ>gOQ~?TA~6_QBw%{(_E!jMoouR19Fp8*z^P<-$00eW!!(R! zF&pyRLfM(_)nc4)uSAEr~#TnSZIU_3xtKV)!9oUO`MOr?FJUKsn7Ak@!==i9=Ww zkzGS{K(KTR{@y#kSZ-{Xxf$J&)r4W{WXt|A*G8*u@h(NzOk#snRXb+Q;E~|EFe6zj z{HiM2YVd`ZFS99b1LI!Kus+pM)Bd-GAq?Lb2|Yfp1gEzzgmOV36wmr!jUg4~g2Z&N z1;pAe`Us9JTkTe70dHD7YMQO+0nCsJ8Md2ciV(xT(E`ID;rH|eK@7_g{IRdAEB<+E&d5v>f#q+z<&qo{Id1D34_PkMH zAMF$mTuK5POVD)Q$1(yBX3t)3)5do+VpOZwUyetRW0zSn?Xt z$zFm8lOMA6Bk>s!GjepJpo;=Rt!{{ivnQry4<^-vEvf;F3@Wo)v81B&0ztF43qwsxxqvNqM@*sZjGntFoT7fn9j+*I!$ zJO8}@s%N4X?IeC%hK?+J-DH;4RH2<`jH(hV+Zw!*>8lX2e2{_~6fw?z2_1LkwQ zIIz1X>2*@gF8lI~id;Ez_x@N4X28@8scKm|s;uJgL6ikmTz2DC@~&$N5Y%9Q zu2lLj>Ct*&vCdhXBW;boa@+QO19MOexh?Qp(G@%_DkfX4t$ym*Z?at?Z?c|u_tsMh z$FSwBJZEo3KIi*Gi+nv3IP6w~RJ6GvBY{@*G-5;=Oz@X$L3zb&Z%i<@@M2TmR3E{3 zD-=arM?u=>3&DmllrGQ?smjDnhTYy$a*j$cqWnq#%H8#<944cp?i(Jew0zDWKUU(} ztj>A7A^X<*LwKtY_YA%d?Z~w3tH#@gk{&<1`pf(yfypyjAMHlP?LGkm&73il57HJUnN)NT?4K=`ClvB?yB46Wz!z=CF)V< z*`HtjqZ^(3d3J&}2+?hjz^iH?ydzw{yP)@MX0lNK{HtK6b5F4rwS)|@i3As?*U^@u zprIQLUd%AI0R6Fmh{qJbWdVTnP-CB;B1x)( zchb(BQgrcdH#}<9U#2+goT@SVh07V-y<2`+ERrpSxFs|y^Xn;YR*ai%szu+0@7xS4 zRIj30=O-QHji40qIl7x<<^G|W9A-;1z2WAgs3$sol`LIeT3r#sycw*92hx*EMwF=(0{s`D{R~>HNYOQF>%7S<+a6-w#gq(xZcRN1>ZX0%V-9QZ&Crh$SQU^A zi1@}0T-9YYlCC0iB4^RWPi5-PjFAc@=A|I*)eRhjXR;VI)ATVywNH0KyKfc2*KHcM*gW6dXE2#E z9T7MV&7_{H^0?abz>ZOP?9$C+gLm83Dww4>@0No_cCsUR@D-n^|Iz4UP3POJ3Yv zcm%Amg^t1?1qB^16byXNi=~T$$I0Gmjte6a?pc!#j6~?m$&0D*=Oe419npx7XZFH= z!k;l6aO91#4!0iq$KbBWg`@s@9I=wfAj*6HlsRE0pRdv>MfFC8 z44m!N+8zT%&oro(2?&AOtWw^W|INu?bw15`<6KW?+%XYt&HqMBPMak@YEQ}}`y=yH zI>KK@3?ePMYSb8zk@n)S9-mL!<|wIrB@yYirXlRJ7x?ojI7*j#I0?t)R9lx71iZ0@G;^;_iL13%~8$WGKM`k2#B1nRCnm&>@@hL(by34ubI zgGD2BI)SRLtk1O{VX7FgW62bpv$P_7vS{YFl;*&klA2yN4#gnnT<}GP-AxVEm!VgG z+vs8XAH65>Cfkl3?v0~Uxn+VO!+G?Q`i_J3Fn3c!V~~}S#qG9ERC;2(ds8t&A89F> z*WbfuJHKjjt{Gny4ulX+8mao9Ft(orI(oV<-{61D;LGzOcm;g1lmna6iud1`?ruWh z!ss~tyY|aPkhr~jlN8OI)t9sQ-=4`Ji&H-XOHfdp{t@d*7GK#4A}4gKfa!8)gHk6# zb!Oh{Rm|9564uB$;P`Gdmp#Jh*whLJZlXrF!+i5`Zcc%X?Ac_}n|g}xm6j$)1YA&? zEwJ;}m;1<^8>X4t+@P(rxub4PpB#0#d6bVjTWRR#m7%b@CCOrb1-mY*c?oE8?@;=} zA81zKQ|YjN5!^zXq$l3+yM7}9osOc9gk8%P6T|V(jq*-y3x0Xd%;kA?#ij?&VzuEG zg+3DZ#wEtWu`jyL<^vasMlzv?@uvLrgsWso`1HdH?4a^|E=z$M`x~^Ym{KNzEZHpD zFS#185;<4YH-8iEC|B&Yf~^Fh42@0!sl&ju6J{}Fp5kBTyfJIW1tbCqL*gCIuQSKc z-SMtj7Rk8}R(ts4{6q@hGq`;d<>WC^f2sVW{{0U#o9+~8V1`xPR)IoyzijG(g`KvP{>?cEX|l!@xjeH&Kpk^ zD>c7i2TC*~eluocVA-YW*qox&gq6XnDlqXANEQld&P$tl2xn+~wx9!IIT)Mk5k!9= zlMdS(i>aYDy}DKEG)!wgE#hSvIbyK4O`bm0U9{*%-G^YO^G|lx*rstBM)e>b90ad= zOrA0HGB8{;jKc~^4N$tQN-zT)yk7dRA|eKgovGtu=q0fhl|O$=RT3R*dAaNAiSg|* zP6#9Omd6!IjTz4w%lXV1$MJp8u7y|jp52(4me^4_NrTpc7`XS&Tg9q|Mn`t88yp=3 zlw8^+_A5t9Kcg#K^GwZVY5#2&IU{7F_vISSIC!h2cSZWH^_?apQg0?N*rk)35C!kO z@n3L6dW?iLx4J%EzdA}r+<)FCc6_vVQHHqGgzVbSaN-4-gU{9WXl2$tRemHqyMPqxn9o<+NJ6QQo2tAiO5`YD4+Yse%b$@me80P_{!X;3+F z@OX{gbvA%@dPGApdeF@syn6GwmCtf#NkDXy?wxWL|Br>5>hwB4;MiXS$l>aj$>6Kw9uf}1x#S6;?UPp*GGC;i zJUGgM!yf~b!AE8H+>QEptE^bCuE>CGo3VMOqp?>Dm#xN8D8%kqI>L*=Y6@Ja*5`SF z(esE`MA_h68Po%dA6hS#2Lx@8*uIe!#+BX`oj{dHtCR;t{6e~CyECJ^Y@Aq=H0i6 zTYnecnD>NWc13(a3$=@)*s}p7wIq#wYD126e!xB@q^#P7FZ?I#oU`9ceG+7cN zPdEeSf4hU(ec9;99r^Np7w2Cp76Dv{s|>V1cB>Q6OSfRh^U#cUX3knr5{)wFVsYpv z2_M4VXAWA_Dy5scUKX?BT}NPqO04edof|9_&aY0-lKlV!Zd?M_lYj$$FXX!uINcU8 z^H}Y`51NR{Nbmw(Jh3vyIhoi>O48+^Nz8zYN{aOb;U)n@y`sPB9T_dG0hgNLY~+=h zGOD7bF_qAkl;jZ_#T~Va>f80{Ol{K5GK%%d*swHpO8StDfU^&8|JDtoh73`C1jhIK zJY!?jn2sBbO~1XBaXawlM{h%)>#8mJ+_#{J`qoW;E@mId%`E59sgj*uyxy~4dS%w@ z#b?krt6j)HtzKJ&?`$sDGdd?G7W~}hokPr6>Pzr|1jJI=6sIi4$Ie^0FsA>f1wai` z+)z)C%tZy%8;*$;4zRg(@V#FafSX9UOfNdnR zVV&bFZ3H%l&?F*a(&5Tx_!uNHPA3l0-^VTfWOvWVbS>h@0{(wP54a2fgX&y*fR$tl zii!r8c0Kmb;_#glE@F0U*5m`UMI?ntYhZKwDK4=f?8Qk#?5&vXF`o8xL-dkzeMeM4 zfH=FRz*3$Y;wK@0HWCR@@{%OkoU+)7`^dOmq5dJE!wFkZN|aE)pK(-tuf@|}x9r$J zt#b@0p8SHo%4%<2z#GN7$5wo0c?nfN728hG+V=3IcIWHn{>tUYV`$?NRd-AJ(-RLo zrJ~=?WiQ#>n97d1%j`amE};_A8>R;x6*M4?@sACzeW6hQ+BY=nD%SB(u5Q(mq4eWp zo>NFSS2MO72Sod7lEpz1MCWY~xnb)>QtbzvK~t0=Sp?y*IL=M);^2AXyG1)%DzpqF zGF-glaJlf=8DxH}-~E2X1cy+51euYnZO4)b`vkrEtjd&=mInXrkn1?j(Vjc=wB{=3n~iS&iw{#dG6&2X2aD8!r8kR$!5^}sY(s5 zD*teZ)*23@w5kkTEwt)y5Y?*n;R3F^huI*}PkrO1VldG|*3xFl(iA!i)6bGnBd=Uf z9hy0RMwrY6ZBT55U1{l6^JZo{;B!K|otqujUydp)9&@U2shGS0gQq`Lunr2v%sJjJ zl+zo~!w`D1Y4YA5k#qxqZJ(X~qmvCnZ&!xzAw7niJUUERj|iHsqgwEZu{4>48+2dK zk+6GLaBzG$Y%xvO9e(c`tn5}Yil=@Ftt$5Qdcl`cO2Bn`K^xugmquL5>iy!OVwCh- ze%j0U>qT=J3cg1@pkl!_9h{$nb-3e)60aQCMQy5cTvcI5HvWWb`gj-tD}e>nbm-D9 z?&*r`?D@&aym4IMp?C&155;$Yw%XVq>$81g!`W!N@LGzWaRgoveC+@6C31U(>dV6h z9~Gw~!T=gR%|LnFX5_p5rF;jh!bfAnl9K1GG`!_TV}hRcU5Iu`Kjb_^#8*x+SKSQq zz0{0zQac~6zbT?S>df12oIsf~fva_rg%&;kXW%~vecEqm=xEKn2O*kXgi#rUHjDz~SI>?-a=E-$OmxVac+qF!t~3_^b3x2Wo;lMC<_g@l*U@>q zmov%xO`5L?F87N0RS(i7mEvY+UvWu3>qcR5jY!@vFD1p(MG)jKMabz<@Zv085ck`d zh~c7q=uONR*S$AXpj|cL|EPblRX-TfvV1b!Lr5!D{pH!(+VBOQcUi%VwrYe>xL^b#a!n#>DUWVvLVO|x@KPv0)urLl&R#s+p=mW8sm-Cz% z6yPjP-o!gT4{diy4?Ur;oD!S~bvzL(O2m zp&04h{HR&l^lcWi*e}WA!mN5W-lXzW6nY#QaP&Jgdg7?sAe*_1C39JNC%M>K z1k)^*yk5#CcbKj|iUtmLb{2<4s|xPk5SlmIvIV>xE7)1LW@c>lot|6)hsK>LWA~Pp zv^_6=ZOsVXcs{7BH8@BKWHtrHguu)Nd2JWYN{uy6=wK-+TgLe;?WN`beYVzW$dW5)3QEUnU56-Dz2@*s_$tvm#GoBq9G;~ih8*#*gkL2 zZneXyW3#-mG5w92SEpFq+xVAJq#jDvj2yVuNONgx`TbfDfS3voE) zP$q??6ZmD0kWItS6-}WR^IubO{_3LgQ)KC)>#*O!X)()W4NtMC>qmq3Hr^Eq1uWp^ z<8D$-{o-i1VB^^86R!9j^T94)gj1!m!#u9TLSyx8yx@po<`YB1Ar5=Nzc;^qYr`G5 z0NC&YLgH~%0a3HqY=CZ$eOm@0$1rR%l3K^Q_8Ns^$?(6cmDFZ&GH0{H}apC52j@ja>f(^xF8TS(v9`-XDHscm24 zfE{|RSwYCQq$pJAq&F;7h=RFD0}cV&bPefOi-m1@es?-MQuEy})T}*|DL819FiXgv zNrd-^DukZ=ikWxiYi7cCTPi9MI6O(m$5xy^&lG!0l`LPgEFde2%!d|#b(9U?GJZ3e z;{#BV3HX*wBhmScm<5n!Dc9IuomwYA%;VmF%o&3iv|!Qc&qLzL08{DkYfVp~Vz|x> zBuI(PS0J)+IJhtKCdd;Hm~Cet4`&I_m&5Qo!f1@mF8>MM;3Q-f%8f4PeowQDVVyI5 z(M@pd{GDiP#n!O+h$XU#f_*JyQ7L1zdMF?L0|IE`J8@%CSw=nG*uYx9iJr2JMqWo+66# zjczm?&PwWaiRW!J=hT7MJ!^_`yGW(7!{3xF5rR0H_nJ1qc%IbOr{vl4RxgP-Do&4( zdXh4I>4;ii%Ew=j+uv@gmtqMz%&+1I-v5jY!WaE0OD&&K{Y2+XZ4Q8W!d$q`&~Kl) z%ywDswnpQ~^Bu~Pmwyw%PAdvry!hF6@y?}Gt((H?4LED&V<$I@oEFi*7iYve)Png$ zYSmb;kqX5nbYm&~yTE`2(17q#i}0LLWcUekP%rD0cMYhg2#cMc`Z2H!_bW%blPqh3 zfdJTb^8mhdxC}CTCM5w*)%n)7Cyw2ZR+;V54+D*VoWaSo%)9Z3?UJXlNo7^8{dh0o zg6KD$%m(Q{%k;*HWve!DS0sw{d`Bb(dEC+Mr{Ak_xPs2hHFxIm@(YlDt!9m%)MJ%c zgtvED40gR%F?)QgXc>CA^q&%b1aj%SYzXx9oJ?(aH=F%2N8WNf*|=JVazU}Q5p8IB z?=|CKpoyzBI(-b8t8&1=;u+(%6h+|Elt{Xc7kJx&dpgET?{OubJ-BDDUhb`5IJ()f zdnkF@#Thxbp05&TKfbrRY&R5E0ub4M?!e`bCp+RSqxo29Pbd{CRco zMlJaAG#)!&^2W6T93|?ObiGTe0f~gDiD`EXo%<)z{5Fohd{Q-hu3Q&IKYvI+sAhxhs+FP!|gK7f?K89_`9W)Pu z@}*U6yTbfw?%Rp9>VMrGj7q)sk?&~zS9W-6Gyw^=HOl z?kw`xuF<74m2?(++^%+jh1r(y)8SXG2cdb=y$FYg<_Pl*#?oal5?JOJ#E}n<>Gz|_ zC0K8 z)kY5VDi`8wJyBfsm+;RQE7bY@_U1j-m zPKW`-DNI7LVfo%8B>jfZ>s+7j85-6<<=|SAnk``=~S2_EZcP1G8ll3+o~x z{6ra!-s-oKG%c<<s^>v?!Egf;ixVgD zrT$e4M#f>v5KAcJ=O79?EZN6k*tV@FVATdc(0vGToT}vzBi)aSol{u6TDv~%kQ@gU zdZm@fYZLt4(DbDEDls@LhPvl<(RTRl<6`U0d2s9}WdHn>F3TaK$GLe#d|0KLOGE!R zRsApCc+qY5nHvZK@ZaF{?G)DH1WV)OusUxk3d8SAH5V9Z&rU~WkSzP!O?V5NjaMs$ z-R*;H*k6-8iH3RncA7h1F6{XOB_;E_*N+=4L1;}8!QUng+WhKVOg7kg zOutc8j_CD{Fn9l>sj5VU`A?V!8Rm`kwBCyc@cro~6nBKxbz}IQgZ$J^n5UfZKtksA zf<;WihrMQhXhWPUP;Y95Fk})+l3;*-{~t>=_r=@YV6Cba9+$S)A}v_)fGMrCdQHsu z+R%8V0R;uTpuQ#oWfSp0sSUPW-x1~#xx3opH_Z{zVdPB1WxnCub$d8``)3(12^qq z-f7FG_(_Fy+%KM`-NiQOyh~o^F-^oxEZp6#b#zuA1)(3Y;*NDr+4<~|Saxyu$eaM< zeQp}v*Gu{dL4=sD{n(R)%bdR_h;ik_b5F|@__1Ba9tD5fjIy8%B(4wW zjYZqJy{-j7inloCYQD9?X309C>)jDb#Mx$CeaMlLlC{0e8T_&H0JC^Qs_a8a-0%@K z3dw|bd{roA2)^JNm>I2HMrXM@?>=gE>~>h6eEfKBX|boHaj=cW=`??ewMhG0>U6w> z_|4SV@e6x1q+k(=Tm5!sAEBpJc3s%2&T0ftvnCBT#2Jg`C*6;L)t)vGxF{91sknggS zqVp8Z?C-DPiVKFt6_(-!4Z}KA-nX+3y>Gi3Cq)Iy0JS%B4iQrCYph zKxSC6H9}I@YAS&OUE$27vX<7Vi;@3M#E4{rZ5dv}rhE^WPPDv0)G|56|4tk<yRD-#@H#$eG4A!y(zVl37Y&wkY-@nU#m$%UZhdX1 zEq3$>XTm%$w3HNDH4jBQcbaGDg2uqW%+0>RJi1$(pa{-y$IQ%}a6vO}8kOdZuHxE+ zU)qG;cr_f$70|uJ<@jNhC1^Sn(_=G{4om}Xta&D)ui~j zK4)#dToGH@AMI=h)XWN($&|BZp{xHYXOt9G;P``MN63$DJDnSLrI>Wqz&v+8u80Md>_&uNx28gMFaTRWd1sOG$_rvzC&jg=o)Iu&gmMm9y67W9HZ%J{=Oz7%Ff^ z+#Y=HUY^mftG4#L!RSf0vff}+YCiRwRSS!S7U!&le<03ddKpWir-7g6O0A6A8s0EnSERR=QSO=w0yUc%5RGfB938bY2 zL10ehP4|9*Pe+JJ`d&FF8_<$?)(QUPtA~$+Wf(JVLegFu(c!kCB-%J)h!y19t;I+Y zdI4OUzUQg!5e_l*r88D_Yh2@>Hc*V?q-U=F0M5E3DTKEU4b;hYOCW*hvOiVh*mkrB zt?`E69dnV&yYp({-cU;hMjU<}Hr)K-Ask5*I?xn2orrH?q%;o&o&A6dHMIM<;Gb1$ z+A>}_5zSaa>Q=m!SGqcv#&ozT_Q?6#X7q=L-heshKowKuQi`MvPt6h1Qr|^=i{i&x z^u+puEL9&Me#Ow|WFf5@-&;BUVuSa+y}R4*^zy>fteeN(CJJ|I*MtJ>oFCF=?u{{hyMrO`3f|Kda)9g=Y{AU7kfX^2dtwHoIP*$cnlX& z-fETZ68z`$-F%sU`|Abbdth1sw&x^K%~-2$)G>cqqXW1gDrvhW6RnNS^4s$fKk?Tr z2=jW_B+C^goNH3mG~ z%}|Q4-i}l{((k6feb6n?x1LYO2`9`0$UMR>*oqQd=?tJ6h3`4v!87Cy88<#5_)H8#8S#TD$r1syM_ucCPhy~1305Ev(^6Nt$e`*m92^K`B0 zdOVZJazu&3e@UDE2nAJ=zKFX+^`N%O;9k1%WkJ_?TZs5pfl}e^IE)DEeP(6HsnyWu zqNYcu^*EdnvF|7)$D5$@sd;5)WW;`{pbj;Pv+vlC`Gm$}N07?DN4^wxS<_fjTrwoH z?Q}dUf82^>k<8;|CC#1Dkl)LDs$24Gn&bd^V^do+$||nO`G~*q10IG5Y60J zJ&2LPS?&>%#bPAisi;9<@vB0AIba_)iTK{x%6&!bB;&*ko2Dn~rGvZW_d=sduREl7 z5@<*!0Ngxz;w-xe8RZe6D59t2bT6A0dH<3)St?7fb%bN0KhiZLy#74e`qDD6LAQl% z=k-;Iita4Syzw_yDjk2zpwax$UtwI;^C2MFkl~lj@fKeM3=jQ_OVpwP_FwwP`sr3v ziI0|N*7m#wr69$*6G@(d3rXI6ilMAbeUo@4`qh7cdnX0vA>ufsD(tP29x`j-$XQIB zOQY|@O^NwinEwcuE?hd#JKf9O#Z+jQ{?(wB)(5Bfe%6M9#7_eR`YJ}2d9#=Ij4{HR ztEs<}c6vQ=vXb{D4ezQ?t_UeL!P`&4(BM(_?OAfs@)ks5`It3^9P77E@fZ4Om^E2B zHxsvxf^SP{1;c|%?P%@IkW%?=<@dS;ri3xarWWm7oEdP1#CX5Rwo{>v@9bxQ+0H2` zmG66Beuv#7As}QkE`DC@kM@ZmB9;Z49r*NUnykNxoW;a>sOt~HCjaGB(% zvu68FrakN>uTiAL;Z6Ck{K!z7mvf=rzoi;X-n}Xfefst@amD^zreb*NMpH}4f2e3k zH@FOUc772YKEoOaPPLQ#Kzjm%88RNu$qDF?BCO8!EH)G%eR3Vu6vFSX>6X)QN4^O- zvx0s*a|o9dYYu*SBUU{ogs&>U&f5cPpqnJ+eSXG*SAS)c&*3fbKK%<@qW;JNf^*!;+28td(~}I6GEf@VV5qUQ&!BzI z4U!|A(f#STL~Ap8x@kT>pSBv^{;Fv(JBWM#7|RP&tPW}a9syhjvC!(4NVJp3@V|u) zhucx*vYHaIW5lN>*W%t^g#vWWDmlD6#~hr$f?=AS+uS-|o(9x5Xjjh||C9h14B<18QBcqjjm$mkG7l3h177v!(?n=_(Xt;=u zq0DTFq^id_fZHIJN?=~uVVs3ZQ6>bY84_B6Or+o{3K3nbcyxL&9?MwbTc~4orK+V!;2a4qwGR)tFP~v>Zs=n zu;jDq&t;m0q?as@!giwl^UAWP>M3*s*SQ}t58~v$T8CdxB?5@l<9uuv9!)WCCr_A@ zyKg+N(k3K)c4TPlXA9q196%sUCz-PPV;ydtK1@8afV@nVDjU^u1e8q9^dN5u+C6zz z=jNEN9NBbUa5$t7%d-j-?g&7%QJNdOaQz|I|R6=m=o-@I?AK97UE?!%;V2D zP&%Dw`TXmkESOeWtyMi|Z_4aHT`%{h1qDeGc9!~96JU-s7Dsr9a< zr0uyfcX!R>5r`K3U0V#Wa&Di5E}m~rZy)Yx*XpRuo}=X7qXFmA*AYJ~qopO{JyoeI z2X|Hc>=}wwi?7xeDxUQu*{T!ASIh{#vC6E(e@vVa@Xoj|GuxC`ILqgZWqCj125aLL znlv=kZfbQYe4}SomejIZk}i4Z%h+$FaeBh?*UE?k5!x}E%6R1q4Qj!`DhS0MP{v1WW143b;4 zWbKxUcc@0W8OyG!daW>b-PIclvb`tGIuo_d?IFxtN4b}rf_rs04rY@H#hc_ttCT39 z^uITrO8*o23@57=GvC0iP7gGpo<3$R+=-EhmtSR{YkwtSaXJaFgl$V~5lmNb)g#r6 zwNsoJ>o7V6T9C?#$I*0#f;m=(bkPw5VD$KZV#EfQ**b|nf2$3Dbx@qF^q|lOFPwS(2A)h((zJBx13F+zdF1P!mc*i$5KR{R8XklSU{(*!95VEjn z@T5T%g>qrROP>k=)Df(z*duwiH6&G!ly&FEfcVfpao!U2VBUa-e>Q$`ZTEj=*uvBI zm1|N3ns-=79tkSOg-J(@f{Nmw*49Bmw$GSRlRFed-#~;V78S$wHksC!MvgDuQI<|+ zJ(|~z7Crcf`BXjg{9#W4ATg*tc{NN0&7vZv;Lpckx+`3--(d#73LHWEqZ$9yd)uRl ziU?QV!+AEJDmsbkA(-ZkV`Q|A-(0r$XEDn0=D{b@I29Mk|8Si+@yJPmbQ*pjx``%_ zTcv(=s&Pf=Pg@1dH@N&hRr=dUNUFhvQlrI77BrLgEhufs4F02O^pHu*KUaY%cG_c= zkKw)A{-FiQ3ybwEY!mp;{Bz*36rKTo9#crEsL45u$7RVj!Ya~D5a zItlT~C0eK^2Y1!giKq~|pP%n&>CNsJtJJ2``__7;Pt=*nT+(3wPYdvP`ooL+e2$u) z_6h6g$PpG1k%4!DR0t1rXb|mq8ZQqm3qybf4xm7K5r+e6M*PduuYb2*9~*zGg?*&_ zj3x3g3J)dtx*kR#sbwsl^xae^|KwSafZ5Q1jG%bG9yJ_4|AdQxk_M&ZSkHZK9NkaV zN{x4dq%~w3PH#p8ODsCb6*ozbdthS{KAp+37XK-SzNh>bD0R}whKC&)aQxg<&vgGu zyx5{uHH>=>rapC19jaEQt4&~Jxy}ZlS?6>WbZ$*Wt& z!`G2VEhWo24xk6Q)rq9M`bb7>G7X6=Byd{rdik=))o^&fbsz{B$eITg?46SD8jSDd z!=RJ9PXH~s7MU%D&n^E=FR0uuESodqPr~Ioyk914wQKfk%?>L`n+kC5lkpUiPpfN? zpHy@w<^~3Cs0+)=hjr1c^HT+4GWKh!MY5^PR(%kM*g^poMs7uK=C*Bq{%EW^bEM;e zZDnPKUwH9*0=S!?8MI@(FdO}eYSqNUuwxVS2HLkB^5O{BR~G<8#_sJT zEzv+A`LrOx8l#+u^f#`=^3{rE-nbET9?M#|WBLElbd^DIwm}ki4ek)!Ap~38-Q8J& z1`Y1+65QQg0tENP-Q5Z9?r{5E-TkJhqUM=*X1b?)pd~q=MduROZ1?&VB36$f8pogsG6xi34cC zcx&dFak9;d>VuvcNOTpA_1xI*cVr7Zb+#%asm1E--bb=egig}HaY%Ddd9`BtL2K8H z)4IlQnj<>sD!h^wDaBC-hn)`>=wn<=-_?xP?X>F&+g$u%$@&C7tHM2wKH3FswRrBf zpyBT@e+bi%^-~-W&h2j_&9Xbp1^C<%^Z47h(J1wNE(KjRzi*TT+2P7I>Bq`*_+9;` zmMMEX)XEKJl~~_S-9}!$n(e#$EOv4U&Izz|hjwKorM$o@T44Y8Ha_#+i7fG1t7@-+8`_gR~g{kDNAHXBdI?Ch%r1@tCa* z6v(k-ISW*z1MlIw!{x8#;drWo(H(sGeN(C;Y31OzUzqWJudSK5f-?5e=F7(%RWlF2 zd!PBvL>CgWlxllGe*5ln5^Q#}yFHn(qxI@U2xd+P2tf2C)Q@kcWShjy#JHc<@Lr3; z>UwJaM1kv`Pa|I}2YD|H&}0mZ--r(`$tK~ASjhQkQ)dLKVW!%l%!N<_*fCPf1oxqF zUNxI`e)=Ej{}38&sv?^%R_mRO)Lm{T3k)KkMjN26YlbrPgerN1Z>q|1<$&Zhv+uZ8 z)HO8R44yyxVC}en*_$)+h3I-=4TecK5$G{zVI`!uT}R^OZRLAdSfH`wb8|hV8_WGj z(Cw&Z4TQ0(_zB*@iwY>ov29G|%DOdc zUTFWkqit)n<=Z+W|A-zoSN9~j-}35j>#+=-uNA0)BypgXdJm@cTGd$P9&3)`$>Nx} zQ?8!5-6qeW=cgkXEZK?vvlETnWBd5XxueD|ID_*)z$_^D zE`4u3;9mL}witx3Rpo0&Q8jF9NGG2{-%!xKmK>V$xcgP`(%+l)__@Y{4HaHE-IQ?v?sFNk8v=xVZb32V0_7CIiIr(9KeBEM_FI&l>} zp9G;xa>lu)tezVq2U2_%xt>~Xrn?ete5+_NIeTV$^Zg>Xm5U^17*J%V{AcE_*1Fo= z$sk6uS>u<6%R+sKz!ZVcn%}7b7niNeY$4V>H+hC0p`TVwaDYW`Q>(+C4^d)Gr|X+0 zMJ)1e>dnPkTso4(>BNDItIlTATE4fC%@#7A+qhAO@%>3%Pew*~lWN3MqLegbwT!iV z$X$gWHl9>&Z_rB14|qnwCw~ilmOU+HeKGChWC$lJp8f4bd7VI<2SP1bBzu96r-vC( z7yIa_1}w|F=4yG>f5nAByd&mr65x{LY*7CT;gY}*?@Zoo?_SoVZb=> zK&Ws8IFFD$@JZgMQYHnT|M|dljpLSVgC0-o^9KJs=6rY1TS5UqrDq}hv2j%GZ9o>R zV;dW%mV`1PtCS7c(fe@*x~g=0W%^G>YynZqA6BCv(yo!%m9};rA$9plG)5NO9O^%Y z$WD^!Cn|vRD>ox;>0f#Dr5^N%!Om>0&Kcp@p^-c#EoJ&x7&RuF57oFYg6JFt)0})T z`*cR<*`O^qOlS~w)V>_v{mDva-X>{QlkWlm0XfCVy2P?s$N1gc(y{L|CwS;0G|bL! zyK;$~g>cd4dv2fxmlpO|c|Z)JA=b_Q!KhHbt+SuMXaDGG7l`T6#=44+2UM!uEP ziA-EB#ZJDz-k$e`G=3fs;Y$DMGEBLEdaEk^J3>8YrfOzpNHucTa`ZJtG5h64qEK@G zGi$&TjdJLxrPKbtGYD>PQmNNP5q@3ZcDhj1JB3#aLqnfJ-4JRs!5+2S@(3J$aou-q zpfAz~!{v~AfPGScv|f?Gfim&Y{Ld25GOAElJlftZB##}5b}V+SGyc0pCR*ZHS37*b?yG_$lCO>`N9!p0Li&}KTofgN2-~9S-qKf;C0AWKb#-0MN#vH zuUIkLLDH=Dzb#OhnlA7WgOHFW>DH&gTyev3FB$Lc$9ew)JBmB}&14wEjzDdkLcBNb zeQZ1e7NS^=$?L45H_sy`oXh%@#G$M4%1x>cp9jU=D<72$7i`znU+WvM_2p1IG}V~P z;Z@Y!=8aA-H;3lUt99vPL5&&$kexArBkTH^gOsDfqT7)L2g@R1}W_6*Bt_CajD zzzyt}(EhUPTFdZ=KT-nI;sb#+>}BM$$-Z~K4rXo#??RUonH#MeE3VC6&2i7Zg2>J7 z91Zs77Kc8EuU(g~nIFAv3YjlFYU(%D@Ls-GYz}w$SMOsBEhi*i^CQKs&~xsgM9aer z`3!dR>Ug$B?>C_@vJ{Bth|&(gjYKJJ_1}m}FJw2jDz2mSaa4##ogV-@Yx|+8en;#6 z7dSF&1PP`sC3Wd+(5@IlZ}sDr-&M+KP5aEgBlCCNOH&a}noW^jbh22RB8iosHha?y zfAJH>^1j&5A91q28vbHnlgQARHCW}hwi%Ad7)PTFs(#>Y|6s+*hwB=d?H@HBs!RL;y(%99}ZysDNw*SPia5m2nB@s3vmiC?b`D z0aOW?Cf~$!qWAuAF%QenbNO(_mt3~$IsUT$OnLoLEzGa#!O5Rj<~%ZIQ-3L`X3Gui z>yHRlHqfp$9{1SG(Qa-Kc)XEg(T*%`Q0L??7S`xb*AiI0NYJs;{s4^Gl6}WYdl1~* z)R zp?wJY_J@q}RJcv;l5uT<7Ne3orK|Dl1X^JUX!x!711)ss8SB7XRT<7W0bkhk@HKCi z8oAwx+paGd5lbMlh~f+x_s!1J$9B``GGGOmDU_HNL z6b-*$5RRXGmy7^Ys73SqlJ*nqa3KS!)iQ-OgMyHyQu?lUngjOW=b>a*Jy)AL&V>z` z(MWAU9|BN5xY-ZgTkRLAC2nFO_WyMq*CR7KK{t^Y&!+zU}}zz41+{#J@zJ zSPgv=E|h&79QV)SR0mLn#*;qX1x{Rkur}yJ|RQz$vg0az^Ab2SSpK5f!~BC0a*qZ z@x1^3ShD#V1)sClx)CC0VZeSXwPf(oP|m^9ZatC8nAzpC(%-!4=PS)ADF}nojb^z5 z*LZuvM~`J0}!qL-@LABUa3YAyLpuA|2({=}g5bsP^PZ}z7R z)~+7PDBSAKhx##JvaniS`kn-1gn!iPolB6!Ty_B}k`Do@Iq(^oIeA~+5SxcA>DlbV z@1~$dM;3d$}S3e%?_$xsi{ zsb~!_d?m5Sx~JlhnPtqtXAm|IDm~Y91=BPpwQw%+G!))8g7uI2-$9qM6)V7Pxv=;` zr7%RqAQyUBVIC_oI;J1B>;hDlyjV2yY0_V*V1Y^sE}q0Uz1jF5%9wxE-cHmB(Kv7q~>5);wH-Ag~#F}}+ z8^h=q6FEy|z&H|c7<-gLEdnzpbzgF?A`9hr~haY9H)pGJ$zYfGzSs>y$WKhmI*sP*2s=k}DnnOib` zJnU(83XmDQdWQ2*4SJSu$lJggY}V*EMAl)#IukfDvG- zV3?Zs(4=;`Y7PiZU1&^ufc_d}(2OP*_p465?yJp4$IhLvk2QKd=&uD?oXO*>oHwlM zz$e0+kx@r71Xt$IO5RPhyXwl-yXy*#h4dudQ&9}pT4Q=YlO_s-SS6FEn?}+&C}+i50a4Gep|M=!jF+b%<)-9}zDS;9_rAcfXkpa1Z__ zxjrh8Xsug$NUqV22L-l`^#e_ix|o?!3Chd)P`SKB^O3M>sd^~A9s0~+naBSn$`Pn& zy(BvpcUoYaz{C*1!}1zcj%lG+VH03_@}V@uxi?fD(KUq0gdHa%m3}2DVLyXW=p=yy z=XVuiJfv=hl9kYIb{0{Kppt<=7N#$%EyZrV;GbB_9Wl+^F$9;A%qfNoY1&ZaI|>< z+(FeXhD)XFybfWs>1}t-$UX*FMgmZ~+seY~^p6<*@yh;~_5t9>cr>*0e*7-fq7SU| zG|Jtn-XE8}mwOFe51EQg?226HlbQP~+#s_+aRI*!{wdcfoJ36>iFr)5Qj@6)4WW+h zb*Hc#UW-Yknsg3}!)j8c$)6&N&5 zCUFkcA02=F#naIe$(6N5T9M-vdb)JyV;C=5;nLWEed6v^F@1&_I)8A;zE>(SxpJn& zdrXE+KqG|$7p%2n0FU!?50l-`p}bqKJ5#MO7R>#9IZY;A z=bbG`dF+x|5Md=w@J4(xZ0SZ9*O8baMBUD-W}^Vov`5EZMIuXIntv3@>kW!yUJbe) zsgqK%WxAtZiBci8XZfwqQbptjAk4$AQ3cui3~M~P+C9n0v|zh5Pyhp2Xkyu_;m&D> z1QgQ_(sE%dg4OwqYKvajyN^zk2)@71)M=Szp^-N7-f#Td_@{4rM!QvVr@Hp)%tld+ zJTYM-egNe0@)S88q97+6+2hhR+Dvy;e-Ui_@mbfWzR0!yZkpgl8~{?WlZBRkZ@A<0?irc5p|S zJv#`KTB#^EEBEMz4kJ)i`ta&YX#B26%QJ=W+;+r^TEx(l$?IjS1(Ks`5==kxFI32} zT?So0l%5v50sGi*hamLN%O>ku=B{JB?z&vJMIEW`KQ5;se6TuO)E(C^C~h>?CFW+m z*%@v0M`N;%b@E4=jh&M=FS>G8hH(E>T;isV(2LnP5MA@+LV`A7dr+Eczo9!^H(bIo zV>73bggVQHQG6vCtYA2kq%~6S-IE;kO2xiNn*&bMlMIrAe^O>3P|!n$jf+uy_dFnC zPlHv#zSE)E=red&(tf6Bcz3i;I6c_4UOI_o9BAxZh5Cz}jYM)8INxXROD^mfj!tP< z0JcZN#KBSfTZA6CcRp>2&*}$DW_iA}9d3ESTME@Axag?xL5`piZy;%_`A6YGSP$(z zmt1c#I^VW5u^Omgtmh9XGE7C zfhRuxd3eT)=B}%*N?JjOrFLQftMC5o`Cd`aq14@RJOhu|WK&AV48@-zScG@c7w)bp zV>Wxr1xd+z)?hcZ*(qh^<&`fOB!H1|K0uGqMrzx))TJd|YZPu%?-CeH_D7c;I7!^` z@Gnt_BKWQTEt=zExy@!}N_%!Zrl6b;S`n)H*e0_k8pRa6n=15Fj?G8Ia?+b=(oPaQ z9ARzbAsVgwSyS^^Q@=?~jIBt`1$tNf~*7e53NhgY&hqVxOC*ljY!l!+V^3u)sjFD~4>8Y*D&*^^s&u`YwxC zfWT?M$*-RE&gJ*nq>Qlx{jS&8SRvnwj>?*k{li?Jm)VLb!8VuMmt--nDg#b;P$+du zZ8J+>P4dgkp(A-QnKBOy9Vr|(3dA=C^)IZ58Y(L4U#Cdx!zMaRDGQhDk1HcHFZj>> zCoZmR)>rK+tDu>Nf?O%Tm$m8Lup2M==5=%!pMUHP={0(@ODt=PnlZ7#z6qHOu37|+ zRLyV@G!xa_IAUvR6&fj^W^3xTKg?%j+@l_5yWvrowo-Dt-R_u;F)W4$%2XZNa1T2y z9T6t>e52;c=1#maoZS>Ffs2Ew(0W@hetN#9^Pjb*aKGpW6+<-I zgq*Q0Pa*0#3@*J}JY9Ai;xV8_7P%u3LZ-yuZ?gMx`mk?7!NTo0~ocsG@)?;IjNjSI{las^C=gt73v5}FL8lQ_{M68l(pZEJn)Pnh3+zI1(@0bj> zzMRCI-*0c;O4Fh;;kxowTEZz1NgJR3<}EE5lQwKfYKuC9p0ie_6eBTA;U_=Y%bl&j zpUJQGUr4fHBe=(G$G;N3i(P0^IfUq8J|@_BK5E6!0e$skcLZYQTx+IfkG8=s`8})- zxucXDUwssBA7Wef${ZL*U-3F`NLAa?dTB}BjGBbI-jI{ehng&`P+ET=V0^$mW+2pP@{9GrxVC@jJ|EQ}gfDPA?@Z+ioW zPW!2>Y(~`Wt%u%-0UztX(cfItj^=znrnS9L*?B#DLH@FIi@$jK*nHQ=X8p+DZgKE$SQ)n=?5?iObBivsyniftVVzjQrSU_xArDqWQmv z*a)rFW7G&ET+oK;K^XnbfVOks7Tr)BH2-F9l&>HB!`N=8N1P@UP;QqN0;p8wb0RGwhCP!c0Iex5Z!*xN|L{$5 z5_wMe^#ID}R@mAJLT=b60yDeI0pyGi(cyqcc{XHi)_C`LQy?Sb&n2>`b=?ZTcR;hS z9uyiHIyyKwn1cfIB%-D^Cni#x0sbI{;=u2)aPrrX@z^rtu#57(5vymm6khqj$*@ z;1<%pXZrJxTyVJmT4mx+dO;cvg>1-Js2qK z9R@=314Ij87yjOZL~f?PFI)>Iv9zY@{zz}>CeEX5!5-~61)C3Wzmm|*AOP&twKH9z zi=ud+uVFLe&S5%|?^1c%|cKIe!(QtODkg0XMk{Lqcrh zCC105A7cH747j*b5N-+3DVra2V;BJB(Mi~n*ZxL4&s%@Rq()q{QuiL32Z~6UxjFsY zLt^ef993xzQ8(EsSLm*lJC4Q!eyO~gzFi&s zmOp;=B$cP;YO1*O=63nLqyHZ7G#M0nB;yH>zKg~+OdJ_aa_squcs<=|f%(bNWV7RO zrfEK<<98|GmVJA>X19@;q5CSIbaeG+&?GfF)L;nt1-p$oGOn_oo>GmZw0anDcrGoFGML`dDs zIm)ag?8_Sf52Ghi7o-J`iVRS*hMqEFZ3OLsN8Uf)_sY_d&S^;URTYCp%?QIBb$ay# zGNnH%MObUJ7*oE<$(ziSD3a5N9J8&Xfu_WIGRuW8s@Gk9IENF}FpJtMan)g#m7AQX zeeIPDv-@HuAfLuI6D3_?f>DEQjlBA|i#fVrNMVt=rwI1x>RG1T0+r+=7c2;Qj?Ufm z9r6+xbA{~oy^8mZeZT&zXXJaGK1P#+?0pJl+&gxe<92JyoXp$Ws+V3JvY{@$V{U)q z+RIIM##lwwYwqUEIFAjD_UotEkxLb-T=FRM8tLg>YrNX;@N~YASYY1ofOm zNXHqnJ4rX6Lyd#4V$my*3-JW+-<)X?voV+a;~Sh@ak2IZv+sk!pgM=d6-M)q?^A@Q zlu>f=oP^5SKGopLc5zVx>X!&u0_#di%wMVZ)`C)|d+ozix{X$tdqH!+DmcTxh6Jis z3-pbhY#v@+>QPGe%Kq(fJM#M}c;*2F+C5~$m+Z}J?C~fK@qCi?ZpJ zXEHG6TPyWG>+5x{C00+F2BDQlLUIomUtCVVg?n=dM|C$A~GgsxCyBfHkmkK?V6nb^gW_uE|5iH;z3nU<0|y#Y{xSIrF9B1pGN(>!8@q;ba(k5sybEKUAHPniLt z#=Qj&}D8CxXu zPk?+n3NLTRX7L_wxBILqr_% z^>%&YH{|8m4~svTVY=#%B>&05N0IQYrvuJs zQAJLJd&JN`^Z&WUcDcSLjigQp+Fpq~3gy5~+GC?U;MbYK8#l|<=`tId!_ta#AFJ=d z=eq}z1}<~-*~L?3>^3sMqgA$5uQXYxYopP&x8$%EQ^m5Vc?>|q?3>Kc<2 zg%f6;GGalvJbXd){Ru#609oeP?}AqfbZOYTmM+jLB$mu1<{Hj9z){3pK@!QGU;)cs zy0|zjAeojW<-g$}JR6vk1|l3fH;TDqQ$v?kTKKXNRmNnR%?}e8E+LEelo1Xnjw7&p zk8l6v<&Q4wzs4f+vlr(y%LCFnUW(&I%Gt|XTt2PK_Phx>=8i+-fwg+MRHipPGIm1l zX5>PPnf&td^5Q>#t{)+MF^496m*?m2WTq?sW7#=_fA9+Y?(#WZ+?!^(|FN)=zC(t~ zIPD3+#=67{2eeSovQ1ZqxH2J!Zx|Q2aGqibkUP=Zy z`gnrgek-r|7%7pt4)7El(tw&l|BLmxzNGkhYQq+Qj>`>5Y%=R%Cvk@9n`|4jJ`QP1zDjQ!sPrZBr;u2B4GL%Mm* z=Z*WYk*N;6YV#BJd41FT5g}@T5%wl=|I`GoHO|=@r<>O_3&0o4yHN16yCu23XF$>` z7ex-IbHF}LE;Cnr2{6|J4t~e8tKBW)Pn&sE?w?wz*zOE)4X#mvd;(Ak8t_+)%gl;3 z6|vP&Bu4Nd%)@3BE`DOLy2eq$05L^4d>Qf zj0Qof@)1a5&qu1TEW%GfvK2Xx^obkEZ6SJ6w>DYy1xMI@Eys3^8mPyTjq)r{t%8W@ z`xr8JuJ;9Q36hsWJ#Bs?c7NAgVs_hjJ-WNL0Q*Zb$hLTV`}!3VvoAVMH?!QfJ{>3j zMb&gP)OcI0CU(Od_{2gm!8%Z?WH-uJHK?*gsLi0s+&7dJWqBDm^Im(hDBVS7U)cMi z7nBXOP)Eonh~qG%bx9m$@F6U@O?LS2j??#a8x}ns*KVadU2c#~61WVG5Ba)-ienXW zT~*>3yg;3ghL4@rwFYeY)u^?w9eDCXeL)+1JoWX)sYQ9aY620S@=k8s!zTV&Bl1MD zO~|rVXi1U$bU|Yw76NFGSptQP_E@IxmrRHy9*b!#rgR*z_ub9PaeU;M5eG%*mVQrq zRoe5%$`NAOHEb6T;2o~nXPYk{iZ}p;_7uFmjgBXW6+Cgq_g6gquBv=2E~S8ktp%ev z--%QuQXA^eLIQ`V;HR3ew!op9)i^xSc{1Abmh(^n|FuYUgPw%OKt#${Re>i4KoYtF z4A%w5S`2wPKKa&>C5iDnii7`WL!*jvn<+&*;&KbPor#ZIg+KnAyBDbi)6QTNtB&-# z>GNvV5?(RJ)@S$6fZMz735Uqxj@+RUHO&*UFW6dSk`|5Cu-}Q2`(WR0(&8-k;*$%SH^cQTxm1q=(xJiIy1>(%((@Ryd@W$$w z1ACn4H#yvh8w8J>aLQ+k;`(lGJ%3KzEZ2M-IiaRqkQTtfVa=#pz-M(_X(*=qAJCT1 z&*LEdEdMa;_C>6Dy?z@Ve7G#*2wI9B%4kUl&yS<&aC}%84Bok~_)h5OcV_j`k20gH z8e)qSiIP^o4qM!Zd7BH-sW6_ZeEs7cKtGs^X!b5q$j}krV`o_L=gl7bAB$o7?0K-G zq+gRlO1XeRoC`wVm>pWuu27{@YRnvvDIU=dA0KgfLw#ESVgqTK1R~}u2^{1x8yX$G z%9&Cs5%J%f%^$~XKJ4Z8%$iJ;??TDUbWg~?otoVa>}77W#xZw`CK~V z*5eTX5$siIKb8mL8CeccZ+YyzWZJ(fjQY5JZlA1?`P^06lKFi9Kt3>N#>YrePP_uY zf`G-BYCcM6`pM1XD*~O;E2e$^WF6yrgE(ts3iR4g{xah4)s>*v-Yr|&ej_V;ywRn@ z0>@*a42zfu-o#9a+rI_p8W2I+NNCLC!C;HXC9GJsiKA7Q_FWwLnb#ff#UMD;IaLV= z;yj}P2-0Ec7!rVJe$lnAv!))kGk@;2O_yhiu2C=aB2XPn8V>hxkHC6mP9~CWD)eaQ z$+H0F0zN8A@<}3?y@r-ow@G3=txh)Fdnebon!<|3R*`RO0v9mW;AR0UhHWs4{n{F@ z9_;hD2`v@z7bi)A51<;njL8@*u)hs7B!+2C%xh!@ju2?S#qOtzo%oZzCqOWx8FTKR zAn+kk<$)e%>_zjGIgKP{J6Jkm%^B&(*9-KTo!}J_0o_AV&>e5~=3((nUuwOPOi*PA z(~t(5drPLHPK9PB>E61P0-~{c`lxap|UOs zr69ULMIbgll&x4*iH+DKgjW$2=s-D$Xs9bC|8~4ocxG^eK*WNRX6W1O@bZhI#Idv% zKqzX1ux~N^4u#Kaz7gyQ<60VJV+VMDS0D&I=Z+)o|%~))7RHmv(D~95M8zAk4~pNiI$m=4cE#=c5}rr z3_U75x5xQwa@*@S>jMHh$7ysXM8_7tbu{rQ?Y!~sJyS;PyW&b26!UoCw>sj{aKh+#EO*-#RiSb*GfOjd})r$D9yL#YlHA3TLPNQ@i^ zt{!Y=IVhK64l8r?W9qCjY7v0j%ERN1lg>n6Y=jzh`*eQFD;iE7l0l!{Vh~2$n7SP9 zf76wq${G9)l2@yH9V=5IIFF$yhHN$k?P_Q(N&cf2Fu;NK-CtO$dR-%OT3tiCbj-`c zK05ksBJWI6z3MfSCsP%Gr~Px8uacqV0Eph~yKOCb*>Fzpz(efNzHYfj;=7XfSQhJc zoBc;R!Lywl`@umk0_Llx*VW&#+!TTWTi zG~u^$5FgZEiGRG12HhE3TTgAPlE;u_}5n`ya~oNEDJw}Z|JX! zXkfp9&va59z6AL%g{*c_TGIb-xY$}|A4xY2UIK=GbHQ#FR^(<`=A5zI33efx&09=8 zAdl;olS5AdF_=E?0p;^4EO+4yBK5k}mR7uxKCch6Oqos9!pWp?RN@#|1{_oqa9ERH zuwvS3m=Eu7HW|2?&u{Y{v4VZmbYw)f3iEziuq3&#-d3U0#T3?)HiF=WTJ%gU!pcsD zE6}@ElBv=F^5eE*M5i4}Ka%yW>;R8dV<781lu9%=Chf_409e$@_lG`dWe_cgY25cpj5QEoUP)BhVtECUD8Q1c=X?_Lz)Hp`mMoX zdaI=LQSFoRqb0l-DmN^1&KiG0%6BQ3Gzi=4^ z6@#Qzw1Fl8Ks}fOfIn&c&pBd`S)}Zp*C_C&ZHL}9uq1dbx8MCPNVr9`odtXsZXhi! z18GPM=8oZtl@oO|+=NaH$)g0I(0uwpF)sMPZ?*Ir)CTs9ix+`fDD`jq&NQKnk6O8* zx)EtAHsXsx&SSG;K=>UT)vEsgE6sWWu7u)ORliz`9|hXQMDmr8?8Vp5*$brrL$3%QcDbFL!D- z?dEN0lL7}C24`8Szj+Z&8{!V;M)?rnWkOWPLWa$rdy24u&$1jrnKt!{d7(@x3oTvc zKV3Z-GimBiGWrK)qALWm4413dyJI#aeG4pQK^=|jV$gEy=q3k(7X)81$fn&?R9oU& ze!Mm@pm+6B?3c_HE??fi<);|wC5$GYKY{Tbtm3^DcEzCi-Q78a;jcW^oZq)#Gjfpt zMZy!BwP&J#UvsF>LuKy=o?fkxF!TWOEn~npObx)ec*h`%?4CP7o=d`70<0OZ%$LMB z4VU^WodEp#y0$CnI`r_%DUsoE798cyprWs5DDaQxaE@#0HHvMCXYzp`CRC|Uo0M(% zo+d3hNHRFph*11zs!5$Phc4lkVFO=#&HsvMVqk=knfsOdAx;n)&tN|Y_if^^^|6e3 zds!(So?O^6@x$mCRg~mDCA;Lnfs$ObYR#ozG1Je^a4MF^`ow(MG|f7Zuxg-&FWvtU zWritd!G`D0%}x5WT&4NgLw)uwOM|Dp2L_0C(3%hp()#~!3_zXMAu zz=}_B#h7XCM+(gMu0!q6Jt2bFJQ;~>1veCFLn;y=E6ccGqurF|HB%h+(AQRc)Wj$X zZL(I>X66Z2#o)1ip<{K_BntM%A^YMTx!a%R!HKS0{Mxxw{Nr3cuyJou(z*|x{0Mv$ zKdmH1;}YX}JoKnfwUOfVeNT7P78!TUa_*z!1kYh8#@W$PnSZ=9vjK(*F_QG`h4j6=>i^; zz4|PkCc(5`HkHZLR<9cjwn)pez*>_CQ;8PCJgsf#5@LxlmKiNV`ZM;pS$_#1q;~TN ziWefN_ddXGl?Ff~v#`jm-yn^&!?Rip0P8?SmUCc%s;-29)C?;xQmL)k7*I~|(tKSu&`q}U&vW*r3 z*d`bEZV}%1hXd0t@{Am@^;l!M`9JT)>H?)@6)2KaT$CA*g`MWUUP%~v>4nI4*4!2; zEa7wc;O**zqt_o#vZ5fGpGMGolYo_)UBFe z{4v{3m%9&g&SdDj>I(=D*y6XQf}kk4twp(y0&gZ4wVw72y)+$nwl3^Ry} z+A3IJxSjNbMxyj8D*Mk7BA?nC4lE{ZCSfN{Q!i*kO*QVoaX>$wVBo$+CBiq)4asSN_Tr*PxYuuk4${V~GmCD{_Xbou+tQ{>`GUmrjs+vU;{ zQZbD3fN1tzDXXC0nbKhqSzFao)CYibo)0|;tr>Y*%K+p?BebLdkE}+bLC#Q#U;&h$ zuKF`HNFlKF3rK8gugiuCVeEqZ)h3(0wDFRj-}zA5Qqiz%^;S}IDM<+mM1TG#@H_Kt zm{4C+04s0A>ckhu@C6Jtgk7AQp5>8%8-47j&Wd&vNl01F7)grc9HZ^yqo%Kc85CKIVpg{f;T%i zx94eBW@?~&Qn`Fw2O6_sEBr~mgmYI=Pa2M~71V8HzPXME$@FE^RlaPX-xKMQHmj;( z!n}n%_6Yj957R+M{%XgWuFO&v$lrx4 z+nq(H;X*0CT-bkY>i4^q2kyQ*IWFcW?uh{L$Fq|D7Jq0{eYU@3(!GlOWO{ zw!I(@An;YefPFT?_P?UYlyD*CSG3;(?+Tkx1&}ataepG`p*sW>px@fcYMZ=q!{nD# z|B2ClPd{9rs$u?dA8-H3W!cjNWy2*duwK!l@RowF{q`&7WgmJ?a=wV5fbtA?0>pyt zT7bV+M(MoQWy`i#PARYfP1wzJJZYMZFy(3Zh5l3pNJh69cr{ua1zwp%@RoKVC3fZe z*Hhth(;o01$E8m!Og1Wv{gz{XgofkT>1U6-# zR>~cSU)r_Ose3V|r5d-0nI{iI;*#=*y`#`E@FazwX3ytci|NN!dB0@-0@hUC!>L#AMB+dDpJy4q&2aoBccUh zQ3a#~JpxT6ReG#=(iuaLwLe(ArT7ipk8=h0W;N_PJ|X_0A<0EfHrVEwg^3KM~QM`i3<jFW7qZp5dRr_{t=draG z{7ydVj6Z(RT#5@dU=_f<1xdX@xU;u6CJ9}`9TuD%rU2tj*5kFtnm$e3s0wi+^Ex>2 z|5^aHQ}w{6ZWZM6yk|SQ-PE00gbkCsLXR13OgtuxEMs3Povv{Npsu zL4h}9AF~;7hMuHE_M-D0EQ9m@P}6!pKlAvxlBrZnhTT~AUp#5R@!aZk^Wy^^qvvR9 z>z9A%l&ajZX3JA~C(sdjJ=RKahGcu$-qEuHsCCi*Fy#)FGiDWuR;H}-s?#iEQf_Wf zQRgPGFtLL*2edPjw$qCXLbM;da-MS0v2|e;C`6HsL`)#hwX>?K_tw_djST0C1eX^` z;rWW}uNE5|6(=w%i#}WTggaVBUr>AJ3rXkBs-Ny;{Kpp!OQ=`kq#lF5wS4*R%wA-< zjrnm|ZL&)ohKHMjR+wEp&(ND`9l|{|`Mz~6##8_0z$rWhEhzk$jnZLE7-=>3GAvto zvia5-UmIkz6=wGvBiQgq=kSoByoQ8_-^i`PGJ7?KAI^W}5&OR`z1;d52Rawm8^YACF3c#UTOWlc-2vwysYJ&b8o7($Oq&W?t-ApG=4F!8U;JtTGysO zpoCff4a7Ol3P;tG$5v^Kc$P3)X5;tTm2rV#&YTrpR^K#8i@?b~InKNTj2zbv^ z(o-Z#5LfxK0Ds`xZ6`V}O=oq9F|_X!pRy-*=!_rv+nZM$AaAl~Et+Q_pI|^TlCeNf zA}QAzK|?~*2fU=Ail3V_Aya$qZ2K=afByHa^Ihr@<@$b*+1g4y0E%Zt>GEs#sJ8ur z9z$wsyy9O*xC*G7Q~YI1=O5cvV}~FZz?pb$T~jxv-p~*8cLBj=js#e{-f%gs z0iv;X%8^f>8`ysXXXQ>VBefu`dB++pew1@Tf=~-zzC!_-*eqF@r3=YKSk~_rF$<%x zI;~6S+yB59=uHBZKl-DZ_mpnTEW8~?7U{Z%v9@qS8L%pgv*hrv!c{zCNa150&IrRX zHmN)u(jWjgb$28e=o%Arm z+~%H>{aacD?N`f&U)QRAsIZf+>FI>|!73F0HG2*sXN{Dk_>PJpsYJlU^>@#p(~<*q z55gZF`0!TRyFM4TN%phfp9zX8&e635fb@0D2Zc_@lAeiI=#N({N|rR9c>7S$<%nxX zb-Dzn<~)7NYWGtEL)LTM!f*<*mDg+eAF-tVwfhfbTwI?|o>I-4(vz7Y=X8LkmbEn! z{|g{y{pVjmb#j=xSjl@BWz~v)S`yEsQqXHYZyT`dAE448TO3zY{1rBWmAe0|#v^np z?s1IW;DDHTbM@@fD@yjj=?s5;{c9V#=C#$sz3{S~!8`PVRa}pCy%U|za+odzf6rwW zvATUVQMS0nDcViIlg3C!Pj3X14M1HDd_vgk#+H%|Lv+&7$4e70vUrBv)1~#?&=?M* zWWd$kffP_~C^3Cv9yXb%C^M$WEV&X~2^94863_~S8*Lq0u7jz5Hsab17Qx2YHU z=z}pFf^bqVRgohBj9akHS9!{e8V3_W9iSQ|AM`OG{-&OZ|ExtU12*HLWBsWFwejcj z2eLe7!czv^f5(Wq0CCJnS?%qh@}?q>h|O|-4dw@&8tvpm4jZwmfeJI_ZU+#*Iu0o; z(4aWJX})@sgnKp5|JRWa#f@V*>^3D%1`IcDP2BNC%aTD9?K{o8n0Z?G;35 zK*080)?VZpN7R%nrGxq;0$Hm#f`?`4vGji*=0w<%orzr(DJT6wi6WbH$RX~yEif{G z${brYENxvMBf@RGj#|WPniuYb4GpSw8f^;~;?d)r5`}8|6^$S}o_BsmLZh z=uw%W1!p-`q6&gH>3G>&p3RgA<$aKBVxLDt^K{sN2D$wzu&DP4yuOJiwa+|l{VA<)hdaUpLr*ZwSICCFfgmA5dS4p-MJ#|7E_^?b$ z*Gpf84my_2)nMK0@bGXMAv!~->Jw~>b0GWPu|7WU$y)-!kp#(y*4gmY(^F+twb=C7 zTIPdS3;uA`5)=m^(j5T34 zd$kG2!A*+H7|PLrENh{-g8cHq+&Pf>$uUCScNyXt&tc`;G>B&7M>+#i?>kr^X2$0R zdJ%I?;_eSCz5lVEf)%)KllV57WuToT?Yu*l;q!IT`n@;ReAF?fNljSW6dQHfDR!1% zL6&JSyE1whjH767n$SBc5|r>XGmq=bp%>36Ejd>X*0rub@fcbmwYdO&7b`3#d$!M! z;7BRtaFoRD-DQf!I;S!Ci45SK+1XRjBRzLPd(C^lEhagr*Ht+)`6Wx|%N1?r2nUVz zR24rX(6e<^IHHxfon(XJ@z%pQ4jgSD!a2YGSoSB9eQzJTygf%{FLKe9+B- zAr5!L*o53;JEKoHF-BIEOs&mnOtc9AL|Lgm_HT7(?lPlrG*H(H$;REw)fid1Cd%Po zx`wJkB;lrFTpN&p;i7*|`?|n$M#>c456~l@uh2+`NZM|&Y8Hx?_6Go>OgQRxjdu!E z4Ef9S#t6C3)95xL0h{ucI}!={gSAL7o%m3pQ*&Rpz!~{XEATeIiDiFq)20UtuQqr( zars>_&sGbu{p24oh;+i2J4g^ZSQ%w?j?ZO^%^V0=pSl10K#m zyx0UtFx^Y6dB6eU0_+JR==KkvO5a|U<1B;)3=Ln(%xOgb(>3-=sbbybhoF-UT(;M= zK-oj(*n^@&rI|zb#4hv=UdfyzTqia@!Vf)$<9Z$28{D%DoC>GjnWc{&G1EKBPJ)Pk zccSUZtKbM+O?&E(`57Q_FJ#o1-W^a-idmD=zGh8ovI+>vd7z1tP&>u&JwtZ#@?&nCa zz*9TR&1t&uvR%lT!4nhujn_a)E^D1O`F))*$_qB^WiI=V|j7 z6xx4{NBc4@wNMFX9|dclg*`*z)afM|QIf7xyjw1n-+|Vj@v@pTj)qA zO$WtJLLo#)h1$_F7iMyDt1rAfVEY%<6qc%NxVb zj`OENMBAWoK4D~xO-{sRBQ*Kc_!|eKbY>s_2g9!CD z7RQoimW|z5;b}IK{G;~D&R6drs;q1himZa)(RXr9=(Nd7f@kFq@n`|pCb4KeGy`rZ zBo_(9Zu6}u`3@YEFjEkkAOL6cPe)Lj(7GkuRaXGlekM{+FiKu(B84mGVN+MX4dpJs zZGM?So4^knRh7NYP)63(Q3qiAhLYvSr}mGdUb9t6u;X7DmtMZ&lX{|) z;+&L*7|VFLy}tX$ik@Zqgxlnxy0frGwh@FGKU^k#4`iRekZnAI#~GoFhLF1x7h;NO zh%sILOlOG}CdWJiFZWua0%st9C#og3VF) zJBNHvr_lE;s-~Sjq`V;!U0QOJ({mwMZjS*q<3WoN3)^-_rFwmycfdxH|#B7B< zJQPX=zU|lEbYUcZ;doE4K#pVouZjvNIE9eC^?aFs-&AMdex1v2?qa`Mtb#g4B5{gL+d#(4S6lGnJ<*yh=rWh0 zlE{3us)tsj)zj}J!@fWHdvUpax#AChKd141o#>{PZ)SUl9DAeKGv1jvVT;4{2{&&$ zf!)>ex}~HdgfuCdmc!)rwTkXIvZ6`UJIvRX7-Lj+m+<(CepdNt_+`l5uwnyP*|`(e zf!nd470HO|A^;p2d`gvKTB0joWD^svtXA6=hS#YjW~qpN_)wvZRXU%Y z(!>^D&3P8(t_sw30uy3F9S7a)p)$h0*kO9&=h$Dm@Vp4h?cXyMT67ytWL>M>KlN|u zGD-{^-)Qm4Yxbs~!FpqpEPE4Vy@xHJ19j;{E6^_TSj5i6O`u!?kp`g`2~GW&?W5{P&esmi5~~SWrIe8^)|8^|u3^?5 zKinB!dp#^}etoc$Z#iNBc38GnGttqxvYWe+9@)*R!`@iJg|s*l1d?`;M-eMdo`6PtSd)vySnT>9`vaP6%F8~*a|hj0RcvWR{|Q3I zNeblf&%XZ+j!|L_R82%nOYSaz%-UFhiRpv-wN!2$$V4KT?!S}weHtQ03Gl)oA-v?e zOVQ>@hjZss@*)4Rig9Gtr$?G!_{Tf0qx-arbth_!m#uEArj~{M%6V{4TPZ_#di4)y zY{DkzAgxv?_4aZ8<-1%IanC5Um%<$H?mXQrG|6@0MeFYeR4*DT7n$ic{r>!C55c3> z?`9KMe5n0REb1cUvuIUuiOA!=l&&p>6^N9O0zqZ)yUX>7LmnA>Y446adIC2TJCO#8 zxi!(wk!Kb_15Ui{?FIy&li}2tz?-wr$`sc0c8mK2{1@ z#{{miq-13^e_LR5aOyzo`6_2<&Z^qD(KP*aJ-!lE+g7y}Ryjzj(~F3Zqi>V@ux&j8 zqZR}0rp&veR`%(h6{$Jw%YpBd}SMPVmZ?{d23Yj<*@D?2$Q0d-(uVx9^iXTg35O+n$ zve5smm0-*vi7(-b-VliTXz!G3S`0A-cbNHJkBQ?)91-swi>g z^q2exvcM4mAv1r{E`2G>{U6-+aWn6I7EEneZ+{L@V;S z{>IwPytXQyGGLZNi2aBr0`c$*FTbk5trt3>HK{#XS zKli4D+aL0bO;F@?BD~R6Bho5d!5yVqGai9^{EtPCn_qt`*u@cZ{ChFs9f-<`qT_uBrq$aUZi7>FF1o*b633WJamg`dZ3TVZ?D#Gq z1z4Gc2}mlwKxh1$jstHR=dk*Ea@Hmbe+o=D+Isq}k}+PPWDu}ho|cxTIMmt{9E^}& z!sUtSd+DDc6@YqaFmNTddY7X4caMb))BbR)FQS!MY?^jG`0Ib!dZ zfakfBC*;>^U0)o;i4J|KrCTy*{X(_Achh32P={4hNWf-_%eL0fH86d2`a<)Qxwd79 zp)r3LG*)cMtm$v>%f8@-`89nlhrR|+<2!m7XR>+B(hXl(ec4FssZ#a&-cKSX^EAmz zKV7YNWk*&zNCRl)MW*wkfgjOwcPN;f+g=hw*=eFL;R_o%4QygZA zADf}D^A6?AxP4OX4R8|idN7C~_W)czyZeb(F18=xEyFcynu|fUP=nME?YU&)z#xm{ICZ4k z)u900P8AG@E6Sti!>Rlw&%(k&bIf5HATz?O_V@>cq+l^U5aC0SG=j`$O-SZ>y_C2( zEMwjS#xW}%s~(BQ!l6ROgy3YL41Q0y++ovO8Z2k(2l962Rxs;*YRF~^%JiLY5K26A*^cvO7N-YV}m0>$XtfvcA8g6$tGJdUa zg~sjXEsTYQh!kQ`=OaxP;zc<;N`hrovtbFp;A=$A9=z|af_6@u?4piZm8;e#{Gwc= zue<^QSd~B(LNlc(5zxE$YZ<`00r zWm6@pO*5|Y8JlCfplcuw?TgwoP9Mu(?X%z}K0$=MmD0@TUfwPmHI_v#rztbh2*eyR z2b%(AZ;4F2FuYcZ`GRs8(CuQ5MeRxkO(L^t^e30$Ub>!E?IL*_BrOKCPdTQ1@>>w# zUF7`tF7ShgpsR&2#uq8dK*r-c zz2<@bZpU*mXcS$U$?f%2kuWn8YMiOiSZ_E_iJVa$i%vZq7KLeYK%j@b#F%wkA`?^0 zKg36MjB}$v$$}>vELU7HVE-CQY!MNI=8&-r!HIxNv&IDg{7O)5m$ct`!}nO+2Y|Hr zjX$zkq6P~fG{QOc)(Lmau^HYq9I1VV2o4*8)?3iE@s))HWZjBdTr^o|k7eD6%w@l` zM9YXSEq}!tBZhjT@O*(t@wOmmllIGp$Z!A~E3q+t{rV-Ix(m}aNM@SF}hM|~6H@$ehdO)BR z8KoxsDcKl~dEFs29F7yk$2vK|sSHb?si?vuBmu=lkkR5 zL5i-!Y9hK}ue^xoiJNG(Co|3y0Op57w=2}0WgQTgjl~F_JK-Mr^3I?(-=xrP{(VA~ zZ^qF1N$97Wp8^Z~^~m=E(W>EZ`NYr01fT!*`fpIpit{je9$EI;_>~@|V?R!&{qG_O z0O+FoJS1#K#K?$Bjo2Eq%v~utV=my4#|Pcuhmq0tE)c!={gVzPI5;@uF^$`~zunpz zP*IGmdwYnB_hdr7;YqI$6A_&N1=ZPP`hL8Bjiu|`=)3&uB6{a-S0=iAtKIG!B513K z*e`)eSz?6qs*hUsEd-tK>MwhecJ#(TCyzf0ttX=O_al6ILfCi5J~-#~G^C%r=3Qzq z|4?R~R-mhsF<0!2=-0A-=l;A+Q#h;UK5h0E+ZHZ9eH!P(L92pwCI_snppSoW6KCpC zCY(!1A|zF7hhPGwZg(tI?b*1GcbSduDt7>y>Ic-noK4U-e3UxxvZlyOQc1bnGxmdj z1XT&>U?}5qs(UBiuND%WG%n+g5^fn>4K?OqG4tAkN#-#C)B;Esn@Q9@e5k)enxTJD z`KOZ=bh$to3xDA94Q~46TaOt85M1zT88GPx8#M*DmokD}g3?|f1u|8)%`F)sdN31j zCkP5vqk;D~FoUy%Ic{Sh8Bpz~-urCL3Vd#+^RR20%{in|G$Vizjr#QZUs{0z(h9r8 zc{Ol-GO%+=O2ov(W?Y<{?dhn$egH9P=-o@y<}>~i>;sB#HYW$i3iFf+f%iX2I3gk< z7Ogf{yJ&EfB189>?LMs1%SGvcPpkFX?7VQ}$%bB++~gT`n{}JmZ&!RqALV?sMAeu! zj<)s-VIEhK=HvbK$H(96shQ4`f%PmN8&wn{<#0plSJIj8=BAR)&Kr?(A@9N-W-zo; z>Q%4BEoSS_r>1A+ZuPLA&QIlv=F6&% z%9^sSGxqy3PWdlqu{%5_b({2CKAO~%30~gNdiZ#Fpf~CEYqGPgahkEW6*t zuu;!R30J-x{^K2~G9=}sWASBY#nVwfwi#I zWaNw1k-WE{q{kfA+pw|1#m}q~tt?aGZ;HoCyw^SHO+wZl8sQ@Y`?3xP_wDf_>Tf3W=ZGIb>Hvb5!Tw!rd#Lj!WOnK zT?c4w*rN$b!&_>x-qulAeW0lm>ucJ4{<)0v9GmGMrNVQt>V9Yw)lHK zATQ-RO)*_@dfwGx7Ch*B^|l`+KfDk?{`P%IuScKz&E^w9T^iO|llN=-XL_|-?;HB! zjeC6Eo1kyuw|XR<_yPNd$E;!6m<>UdA8wtyex~B6IPZWv#CCpmLOeLozE!|x0j#RT z!iwrlL26XAcBen1<7N^Z+P^3Z3B1dQoRl^0O7jip6IVDu8Y4w7siyfLHjM&nYX zLrVQ7!I>OM?EdqCd5X(#e~k|b1$hjCh2&=BV4E8g=83y31TPhbulrL!ChPCC8zw(*xsF=%IB(zYL_~Yo zY8QSyd@PWh_Yjz*XG{6yCfgzFd9wnte)-Zf;gk6U16K510<3~7gU^qD3J|j=y;9?` z@nxubj%t2*wmIxi?)2hj>Ac!g*C$!IpWoSi+MbOzsO}L$LJi6DY-nxCobqv0=m{bB z-T^eb-e>#CdM&Gt5Y59?<6q`JU$~4^Jt*G9GSULZ5;%7cAjz_ZIgjD|Uib+lj{o zz0C)>v-p@kgdxlHWIXV_nISPS(p?6N&O5!C4v=_1xIvBa3(XA-KzO4p<|__b`P{MV z^Kh0($o+?l#j+>iH1_6njoKcN7>}F2YDvaHaF0jQ3}k~NpJdGiKAPf-#cM{KfyQr9{l%eo(&qqs(YV)7oMKysyDoXs zwm)Gb;Ju^}1olB2mZl1&1Dfkj+>ETeap+xOfw4q!nqT@j=Ya6yB*&0XKGreTB$dV7 zCjD!ldkTs%*jr5{q1-BZ2kOcL_Q4x^?#p+b^mFu;N)%^eRq&%6Yp3Z)WX@R_s`E=R z3RK*Gj-D^Gw-gm?4#&h~(f5sU;fvhljf*G^&VC;|Nv*SqICD8Bp@r_YBT;Tm9u8Ai zH5<;e@dRE5g^#me?|nKPCgw=jU9V%mZycvWw_R;kxpy1HlnuGn*Uzs(tpOV*wW_)2 z`o!%^E~Ynf4KtT9rDf*LxF{M5NSDYe-Nv+(Qsf@``( z7Ml2~gpr=-htH9|c1prl;|TCZJLWi){2KcJfGY5Koh- zPD3Z{Qk*a|GMtm^wZ;HfjpoRJ-)oChOC+Z{lVOK!%5}QI*_Zg4z46eoa8WyzoB6@l zTFiWp+SN~_rcg~B786`J(~$6j0H$GMq*fWnPnnTAG{aXY;dufv!zTC7uMR+VuYJi7hFm+ONwgf_iKStUMFuL|e4rckRMF4uw&KCFM<4gwm_h~Ye0 z-*>j8e5YhBQx7p0^Z~|J z`)SMi@*1~1ca+7w2vz6P+RJs(+^3sE#XzCie<=6ev6GKS`i91vln{7XZScD9N9t?a zEm`+I2>rS?LB5?>>41ZekEBhRC0MywV-PC*u^S zgQ}EM278KItkK`Pj!nR0i|`@q=f&gA2wep!IQucAWn~E0ues!mL9aQmO;L15*<6Ef zfAw!i9!_s)-)ae`P{|j3*8IQyP*~vKyD8pCpNdvG`*Za&ZY6Yftt4biq2ZZ}==z%f zfnUCXRd5M~e0ZHqW8N9}`%n3h9m}qH^vW(Jmi9n)ZJ&q4ZAF2TwrrpM#@u>+2@50A zg=M3drK9G#+uuFaeUHiQ(KcCc?JiNmcNdjF`A;t=+U|P1kNUvG8aC7hDw?BMm=2zZ zqX+T*@WYzd>+zyX`};DcIyYZjP$9Q@&|g#6!9fm~CfMQ3h55Sxon9hOhho5E>P5MQ zf2q)Kt%4roXS0kX7-0q$_O~Q~QNzKW8V6y$nrpy)hhV0J&Y2H&F@R3(6%t$PBUKl9 zEAen8hV;aI;o5TG-+xlFg-4(|4>oFyH<+Rr^TtT8DDGnFg^tjR6-dsls#woZh2?5T z+neG_iSRVwd^xIS+d9NaeeAy2tK62@hN_~U_WiZMJO%m6Wb$9OIP#=r`XFylhje5Q zPP$jep~mNb2Ma`6-){wrRg4bELTHWOi_hGz{yDw&{FeXI+|9hHt$7 zC~$$ku~SXzRR39~dd;y%B01A;3n!-l>E}qOc_U8d29{eV?GfH%20?Um+QZu7F$6C7 znDrr>5xwJ}`+S9r1GDAwXuz-Khe|~{&x6DFYr|LCef^j2Me${PF3u*tEFGdE`~kJ) z^?bKK%3FSmnqd=dcxxmw*;?pHMSV0%Z4B&1?=j(ZYrp>Ps`9kN?hk(#)fa+YqLl`- zel<495ObY}umj)+#@er$F@}op>!=(7(%`H4?KU&Xf{LzaB&S#Xtt|$YRoB=GnbS>2 z0*4=*BM*n5^Kh|Stx41O)2~BMs-7kRaN}Oa_lBI*pgnGpXGhuwbt{A4)@ff)UDMlB z+i_AT#$iYt*#q_|z9rq&6Mh8EDF$605`tR2YSYvw`?`-OWt~1GdIY_;#PgB{pMX4T za{qf05Hink$TUQajN0ecg4tvcqka zn9Mw@ipl%&&})Lthx%d=6&k;uMPhV&gn7>y`yT@+hh3SmBaAqz7^(AJzAR;_!e zg(AV}7vr%)t^PM@CJ6t39>nqcU#B_s06Fl5`q#pqorn}<0s5L~q(ni;-!o`5<2)1? zU?;-=f39dfsvH zKgN=fyxkish9E6N4%Q{DfDb?Pi!qo5dzBYu@h=B{2JLWNT%~o1cjr3QiN~LZtb>pz z3}4}Jl@i*%P_2g*@ws^}$ahL7A3^exu_)=vlCm^q)InVGVgF2X3yUIF&wp35#5U(T zHAdCi-uDRJH=Q;HHjlP5bKl;*jw)`2A9@XQewy-wt}qKyoKqabcvP<6U2TUZ)z}_> zA$sU}yfU9>pLt&N1~fh)TEDI`Kis)w_4bT&7z~WguiX8+E$5Pnp3 zzkS>%uH>A+`z@NjIEXFf99t7g2Ui_lB;AP}9#7?N`@jUtr+|E^(nC*GQBj?en$M9E zSvv(G&-u}(o`MB$J2g0MagRb)eQoQng|d8XN|WDM0_s&RxClrZJJ zZPMC#1k{t&y=njO7Uc$(x;i$PY8|&Tvop*M!4X>YSAHWD4+AAMBWcz66(29n+2qr9 zxA^_}!g8nGvdJ^5&Ly>$$?nQ&u?u6e;r_Ktb{?Hz(uP@0sQk?!c7=jTWUf#h0onZr zR{qz=F*-CMYtpF4v03V3@8e>#_p7+`tPc0lqma8r_xG!zC2;dT>vx?$hHmHh!^4bf z8VgUhhWPa}7H`{JmP5JY<{}0Xv<+@THD#JZMk|z**~)(}+IW0V2?%xG1Eqtd_!ymZ%Ujpm1idRF|eiV(fC{V<1 zcEfwkm=P%kA+;O$< zZzrC#b69`cUS~vp=)Vt%QJ&4l#J02{Tx+{sZ!G3Frtg@+@O7pyXIUIsD(gzTB_oeq zYjiDh|C}%OCk>SBe*(f%ZUVHjfm-sBW3#!OctEA~Su)Aw%EO0$GhTI{8q>u!D39(^ zl6D<0NjyuQa@f9|;P9!4Zl}J_ua4@%8mT#qfMJFj>yzk8ZiJ5jy9zFIg@*W0%i zrbF$aB*RH+e}u7Y!O&SkO9Elin5v{IN%kh5IBo6hhPv?#4PdmbhF8F88gX(|RgBxF z)>%J$Zv(&w6W}fln_uW(+@ra6dw?|IM*E3{Bg>@-E)DUDI;kSUz{}#8b(HY-`T@O* zvZ6wt%JappmVHjYec{Iim7hxg(-e*QuqUW^{wpV$1HY$s2aV!jCw{#Aw4xu_H{cFc zB>!%E8Z5;eiF*pVH%B-wfiAaqT$rcaQ~i1tt%35sN=;f62l<&4HMtn9jdKT}AFYqh zc+-jpt}+USV`@0mg(^4_D(2+xK}^F5rY!8&XEnKAN9R`IVKO zjm%}`mN9@o&Og0+tFHL;rvn?Y(A3qx9C^ed_4(`CcH2p1VZPl9llg4K1gZJTv99_} z44?BJ1rkip65lpw>Q-NIsT=N3J7$nwuQ=Ve6Cb(GgyDdgPBh(#Q~kmPgvmAMz5-ddviuVku2|2Z02P+S%uVD z26WCJ9I9;tC>W-GS*U}G)M2^g1f@#zFE|ygJMo^MSi$*MoBD~Ck(R2=QQ`duC zv(tm>-dvW4sSbKn)G4ID&78|&+mwoM@6C!HTLrT(fK(p66uR^e*YYPXN8pg(=PKgOF)%r!N~UPUg>S}z4fFiIoo+?S;S(G%{EFFQC!?Y zf)$4q22YI_S;BcgqUm~(v1t{!aQ_6U(&~ZPS4M24J%pUW|@-MzHEHv zPS;UMyM-XrCNb?9r256t%j#{3RtlDHc~g+cUmDAfDs^gns0v(#js$5Q2FrgmM|{qn zDk`uQ>q9jRtsJ)>IZ;3H%MWH~d2B6kfA55;5lvGy)Gd(a|LX87@V`o^QdH`~6ul-` zNh?zu(Mp|h1k9;%uZoDjz(V<%MoE|U+wFTOMNi};nnPTRrh$R=qJ>n1~fn~4STks&C6RH)iJYsGw z`x$837>BR;R2qEd?gD3cgJ_7-{s%tT+TDzS zA$_zlR!$xg;HInzbA~M;t~y9W0l6_nu=qHxW>MzPwv1R=jnXf0w31dC+nsS)CoSe? zXlx676i6iU9|O(#)2M6mNVEaSVS!6u5$6Ftu;tCht`-*`ShnX&2(yqh>A)>OPebh# zy7Cy3Np~D!_wp_HQ|ZKNK?SkHQBL)R9V4oxwDF}q>89sxNKl#gF;K5Z-Q8<4bv3?j zTzuD%KvIlWA^bAAFRrWXIz^q$7xs?oR> z#qcM+?x{F+9EnGfq}}m|Jof24-6RvYjy$^Y1u~g2Hn3(j`F_4;^jGy+xfk#E_^s!N z-GB}t5=ZjMnGFrFgm}4_@hVc{p1{c9n3|f zu9zUnFz2^)O!zt63Wkx@kF6CxH}OlZTCJY;rgNXuH=TkL4DqM!7+}dQnvoN1;oVbz zLfOm|q~JiiLgzN{@#x=!2u_wwgdr8p3>X`9e=G8Iqt?7-%7)aUULou@$BGUAT?cQw z{2Ds`C33Z5#ghIw+k7`(j^4p54s-1LyD;QkmbVPIo2UUJM2AweB}qM*OIauXEtPOW zcom*2Unn!%X2p?!J_Bvr&WlgJJ1h;$7K8rSaS}7-i0IrsWzaDtkP2SnYKRLHHwCFd z;q?#QT+fGy5j+Yw;~|yOjTL?ERMGta8{^kuJKwUhZyuiPV-oMb8a=M$RUqrX zG=t;!QZ8PiXv1AU*DTow?s3_f2yztDs|0Fe{ZzH$g5+tsNk1gNbFeqB)&A-PhiXlzuoR zg?@Y!u#kdELbI4WYKLJbBenVadt6x=Jb!aZ*;C;9xiAmI$6(y0&ZxZZj^`R8T27e@ zWgI=&Y-6pnAg+Aq+``j|mZQh@)YvbHe4mh;mL)Eq6bBMrma~k_2B!m(S>SD<^c$~K z4;-{y=#P=GZFNL7!S9(V@b5`<{sXbTqQ~nq-TGS^1hS#INWFwcYHK}BnnND)@Ir-- zDQ32h+v6Mav1Q}wc?N}_bM{-SqSp`IkfOkr{wngt(JUH~ZnqP2F?D+A^<18p>=vHK z4b}S48-^-4%N?4uCw}5epu>Ag85Hn$(#YSL6Zc2{e@b|#R~|nff|wWp$7N>L0>I$m z6qJCb#&80vM93C7h6z6eu}CECx`79=->dTI?ibERF~jpNy19E?iX)blmd}0#r2Hgm zRSEub5aKFLp{ChgQs!QtqJ+CAs;*ATZ8d@`5#-fmFo@k7(wW$`H?gEFTC89xVk<-J zzsd~qRJT?bCBS|&k-#|_k3_9a=p`Yh9VC?0ds9_cr~RSZ!Gpe?N7!_P zmjQf8#1qlzwj^_lmz&^#>L;O}saT|+Y!6e(rG*;jB;YVmfuob`b6}2tn zJbdcXgING|*~ghqMr_}~w$00l1cCJttb)*U+lxfBXFk-=&B{nk#{AxahU&yZd}|2q z!{3bY{uue~@mEnba{mzhgb+8jqz|#u$Dx>-?q|ma&WICGN(?aW1U6+z`$hBG$1_At zL%6n|G$!*h0RE^@{JQx}7d!xEt_<(9>(s^LNXV2gD&g_ag9${i?h;KF50T8eoTDAg z36;k8@#9f}ZRNi(^0&~T_ha+Ah5jA((1*aBSSEZHD<+kdMq;v%u(cq*DRnX^T>NIV zEn9tfQP*rw+jm(T`}Y<}sS)CC`&sugtB2MwDXHt=M_^+C_EH`_Nc+d?S!Af{+^I?Oz2MiqJ|UsEagH!+VQu$FG-x>;;wXQZjdi~An{ zDgagt`6nIbl7R-BfcCYh2ksx*c9bD0p!RX09A=jos~R-ed@G}q{mW;>5MHO2w5t!b zVC^wMXyxgy6DLyvwDIW>_R;Zg>Rx@$Hk+IIU1eo7q#wr+fv1F3Pol5S;-~ww(k~Ik zNWPYmMEnbJ^HP>qod)K+oIJbN0ysqeik(~rF;LD;vv3F6vLsNb75-^OqozOd1nTiq za2*-{-dtS|U^>OmsXv{Mx^n=gNT@tD|AUS0MlN!$be9Dm_XqxY6U!_xUiM^oTfXUNnNz;6 z{8CoyZ0nb!$t$T*Qcc<>K1H1($56y9W!jW@TDU?pcOd@D;9x;GDE-eI&QNk~Ty?$VSZff{jKZk<9xW|M2y`~n$P$ADpM0BD z+B{lu>lEa3WLvYFoGkP2_*xv=pJoR8c{3>a?J!GqH0F$HkP+!Z9stbn25uKeO&0qv zxjZeBDJELxsP8dYchRuEUaO$%!Qh6&EDi=cI!Db0$%y zDyPaP1AnF}gfNQ#d!AutS&E%W`)F<*nZn;jmu3fgYE&I67DctST>LWE|KkFDh_HPy zMHr{LaK%fXw#=-V4cJZ8TF%=qY!wpC(i6661S`$(l?8-^lB+Z|5T zh))SWBAIQZZ0#6FT0CjY3JQ$9KYx=_O|>B>)HyF>5o_PC*1fZhZ0|H6>bv=PMX42!UBzK0a;6fN#9$WugV+ z@#Kz^hh;Y<&RzLBSDV|0`f`0-4YWA5jlqM*NQBzR+>ey{BVe@YU)un`7l@NmQTZL< zoJRmt{wVxo;@bT1zn_ChC3z#HL+ljF!Y@|p3l2fXA)^1?Jn~?j&KTnB)V@8w*ZO_v z`h(*#)jx{_6SpLwlBIPu%|0bFQ`QgXud53thYd%5{%i!VC`f1!q9w=tw}M_Gr<&BL zoDVXGL+P<%NAb{dalLl-6*Hi)V%|K)8g>GHbBcTEIOo<`EPD~vb(&u6x~Q!vw*t0s zf(wj41wg_`Mb&_5q%qefN77YR5)3)eER7v1##1P^O9SxEXB=+MNo3SIZHd`Tsbn}+MphLyq5G{v&5G#wGkL8FY7V}$^zpdIN> zTSINhTCp@(vCO?1UD8F(p9~ocuC9IH-P<#8?VLE4x2FDQ%_lDjB#%vJ0(61eX~>pW zlUsfY+O}TH7fts0hb*d3!mn(N9+|aL{${sJXLe)R_08U`flaKuZqMIc|LwyCp#X#Mf+0qEuIvi zOGw@jB$H^(LCPUJ#0Um{%kR#Cm2(FPpoVT|8Z`KKH8;xve>P??pm?J@QV&G2PGg%} zXP^H*VFTD;21(2U1a^g=ZSfYQFoMhctnE>()}>$KBcMo<`f5C8Gx4uAVnXVWXF_V&RA+;*@>pWeY{qzWsFm zCVqY`dS09|^=&WDxIvE%m_3>UU0q=h=vYdLZ$?$VDJ=v2_V4#$t^O zwl}XQ%2BAs@6$o1O?)Zr_oQp#Kf?&Gpu*c;z=m=b(^TLhE_ba}9BKh)Dnxf!xqYFw zG7&TkbEQPYJ_NOe=9|ix+~JUD|38|(GODV!>lzU0?(UEdDJiA9yE$}scQ;5$cgF!K zK}xzir9+xSck}K0`NsRtG5F)P&&s*xn(M>t8q)=IP$x>=0d$&5{U4O`H&vJiL6pHc z<1EazMiwbxSf}$Gk%u#a+=cP3813L!X&@?eN*@aTLo)aexGs->1I&A@-JKPDJ52ya zdq!vzIUw&u$2Y;faXYO3L$IW;Uv;Cgs2?9F)s*1SOpB6%O|$~h`PDG6gO(uhATn;_fT*ezkZ*DYfg z+n2tq@9>Lkq>NlNTUp=ldzZ&^#(ipg+t3+4DT?1m6}{TiFQ}mZSs4mq z>U}O*Rt}%Oveq}4vR5}B{5TtF^6=T-oV3G-^RCfHhVu;{__@Ihftam4!qc~o93mU$-j)-?kgr7IRLgi^!PdYuPL+3=SC{q`;{a3dsO zJXtw)Kf~w19~a#%j1pJgH!ZF(VM#S_aRzt9PS>>u=$$N{b8eqE{b$d!EK74NG1onf zgnp78G#NarLX%E{S9w(G?0&3L=LX6#NbPbu356aJ)3;b9oOxdjb$VmnM)lAq?89_^ z^puU|a;sr~jAxv`Ee9rP^uLst2QSrVGk9gbp{5A3`~5&*$qkvFMLV23wx9CEgf2{6 zPpfVCgX}?pee~7Lx5$iOQFV`+U69g1 zMt5#4nb4I~hxN)<@3_g|;Y~4MFSL8qGj>oQ&L0j|90cpuRzT#ki?0?wQ`dHCoBP@;yrpJQ;hHIy5H9uh(e| zSimWOj6nXQFc}?)A-lvs13l>J@R44gFdqR>llp4jIhB>a5w2#&(5%KHbPWxTL1>r1qJ6_Qn^WOEK{Afrd6yE8dREU}&J_b9_jM+WfVvQr(z z0)IbDNe9^O>xEmxQLX$z`izz9h0g<*xJm#hgl$JI?RGg_z?;Rs4hlz10dN9QjzTqv zqO%;Is&Wa187DZ~rLRdaIk+N>e-$`x;7QNdFj|HPD89^`-=_%C4MtnIsH!g+3=_n; zLRkylC;HG5)MyasEyftPOnFnqVK-D;o#9|A?Cu130hRU#T#sS32;QIOyEqqK#yslZ zvRC7j9pT^Es>ih!eMVPYzg75o#@elxOTyjD`%;x5DlCD(lV#uUw@&J z9Oy;KMlQ33-!{d#BhFHS)v@w!2}XS^Vsk(K1Gj3-3savMs5@G1y6BE$q8>ojkN z_%s1-Gy8K?z4dtfghYNL5swJ`=(J}~;y<%lNC*3+fU{AbQVM=0daj2pUI0fhM;KI& z*rWdzOZP-+`omACuANFv>`^0#{ zYWi`K55ah~rZm|LNxonxvi=WG#=@6R#*MCMroF_TjT%jI35ud}my6*jvzCv<>Z#wd z=i-dt)ek=tb&p>8vmE{QG7&8;h)p3Dk28%RQ%_k1QO$uee$E2vlQz8EO!j+eM|nv% zJgu=fjA5dx`r`9NPbpSDyMi^l`E(jDWs@ax92$Ix)XBgYWiuO>i+~ZEq^c?sl~G*r z*HdV+)S$=xR7R`n)dKqr7*olbP`_JwnWr}(k3sPacS3&94O+}h1RbFX$m>t}GoKu> zTL3lK7qQqVSQa42rOVq;|JBU_LYsM=Uwy>g=Swgio2FU`5YeI*2z0xR_2oBIp1wA= z!S;NKC(v69nFjk54)1N1q+}L77S?qs`|Ye#heAkxnVk<@(z9<>zwC_3{?-zjpG#2o zv`xSQecAA;4J^!qD)dl%-xt!;3AkFD zHdPnmGwa2x^f6rn^9t0Z%KHes0?Dg1h9N>cL)}XxbF`Yth?R_rvmmqx=}1KRVmAUI z_#<{}OvCR(xnQZJkR%z(r@~V*&m;%*Y1FO;j@r?+VR+JPm1*CmS6UmmajsX zF4&DgGug^$ikPw-qEb?TBgr_7PG06Mf+YuHYltmY*XX24-;}C1_CyrP_zGX*4cctm zP>JuxB8Q31^0tXVMkkN>wChzb%=!C_VfC{~9qiCOr?i%iSOS*(sIJlcs2b=dfj@nB zbTx*=fF|tai{7XYEFdFU_Qktrr8;=ADq|w~3jJr=rV=m%Bww8}H8xr=1@o5Ff~8Vg ziVTRL2~LLs@t@sgN87lApB8MDk8iVC_O^I3%qOOOvq3z2oLO9i*mnoO!w~V8PzXw1 z*>kFjesR``8ht&dI%$2kY@qbVUV%N2Mb9d9R%AmuS3-h ziN_V2n)`G7nMKOriY(p)vxRQV!P-k8$7OQsWC-S<4&MLwc;|3W>bDlz$X0ZGycK*;b7A|MYAE;5>-run5an(IHNu>gu9nB6sNM04EGnGq+}y|9(Mor zX8!(Ys_8sr2n^^0d;b-o8@)Hf+I?4F6w71;TJAFl0VcjlAceWmLfox6pG6^>2XghD zM`rE<`~IQ`^^}j2sjxsge-QquND|6tN>WLwTgf1eBrH-JON4PQ(EM+cLNy?jM3EYx z_;jfZV%W8`H4*))t?2B9a4;_x6YL#2BGY8_@Fzmf+@|+^TZSR*WPp&qIOduJM)A-GFK#_*(H-pff)@ z_4t3pEohH?n!;FjbupteF)IUNwv}~nw;%s@GqP8`LU|aM^a!|!*qoHAM)i7=)8<8~ zD7m`1?^G#pn)`5OHU>$Dh60fygbr83_?H_J)uX45N{GaiB$cI)%z3Ce%-#rh|3}|8len zMGU2tELW&hEm7x)Bnm+Jw(HJ3@jZ~B8`y5Ef7`j#p}rQ}Gax*;HiD(9%TuWoh4Q~2 zPJadMxMe&LGg5=sW7H1>!X3Xl7@oQ0xDb30NFP#ZR)L>;IsuRvGsj=lKW+sgc+cNs zP6-jULrl?(jnlK^&CU1Yi1J{>^4v`$m_9ZG%;^Aql_BGkk4aTnC!CmvFynUI#uIVs zb0D2fbejEqnR=8Vwk6syTP@}3?ydHBL}S!^RF8;m>i%6#+Uc~HtV(iw9r&a z^^rDZW*(^Q&nQ!35myn?tEzgm?*&Tw1~yZe0I=nwG3un@ME9Seugg2ZDtaN^A(9&u zG7zVl8r$* zlJhPk(_Tg_?i`$`)cF}JdUh|GG|q1pS)ck1F4d{Uc*qz>>!@ziHBuiB<$Rho^6Z;%Snf^x**a0PZE z1I;=;q<#2GPfpS+uZx~@D4s|n;rcdU*2_)#y9BlHi$alUH3T8bOEzX-4QT4heKBNX z`cK&x@c|72J6V?dp9gFV^krP67)Z=}-ukBJcN;6IE?t1RwoP&aiz zuY~B<9bcF&lT6e^NsTW=%dCv@5AgVGwE`C_x@HV}u#KBa-?UACW)Jgf!Lw5@?k9q~WW&;*uuhO$0+M#)+A;DgmO zXY?aO`8qEcv>S6(!gh+uD&!{B`ooOp!cE4;5MHwTcF@8 z&%=i>+lOoI5aV>rrYE%g;YpS{RQ4w2d(b*aVi2qoJ&TDyfxT zW55~UWAJ=yhf?4{T?J(bs5*!O>Mfo#NS}|S?*;1s7K9r!)uXX@Rz643C)p0GO-leg zFJ>4j#^k;1}wC4t2Sc2T5Ip-sQR|IrZLbwO=l)^csJ@##q1(nzog ze7xEC|6_A^zQrYBQ*|2N?tIupku!=+Ew$c?RuoNhhgVqO7 zi+J+TOnq%A5u#&Dw@vuROQ=&sT|!UudK>Z9qO$Rn=)F|v6hehf+vx>fDArBZCvcGO z1h*tc!nZrz8AopzfHrtiionI)YH9nZ1**R4l}en&Og9*}{}o zYaz7rbh>;&XCmldt;HI=3*u{r5gtB`I(Um%n__fH&7_%>v|@zawf$A{Jwaj1 zc%P$Z6evJEGC2R{;F18K>@+Z0uz8xdS(m>DOYxEJ>s+2wr9{$y)}95}EJ#XpV>tKi z${M!|C`9lh0bO5LXG}yuk;|u_@J_y1p#BNWfCb>n@{EO2puO#Pw1{USmDxs155(4* zbd3JT%DPU#$W$LwAYEpA^W7E zPnkas->Md7#pZKHdm?7B$*Us{ys2KR+^RcD4>6SW7ke?r>0h+&)BBnN0Dw2*JCoPk zun~)W|3UDkb3S9P3UkF5DdgF$I|C2W@$ac9S2F%=>qt?asKxBTS%lOJP>7lX>6?Tj z?(mUH!U!amugzy6cx17g*q8NQ7S`Q$Y?nAnU}{~e!$k|nn(5@3x3_#7ZUEGgrz|gy zzOSBT4tF;D=7=Z=>@o*3eXpo$(@U_x6*~E1#{H-38%%p*V93yi#?dkQjD%Y*O0G~O zZmql%&xjP!!yxVkdxPn_xKE56KA4SkoA-yIwEhW z)!nBnUP?DFnooQC_#qmX1a7Bp!uv+NW9?DW|jJv-N zZOGU$=1EN?$FJG{W{r7*r}L#6vQv$JQ0SEp7UjmDRMBw{SBS``ba=C|9+~ua7SOSq zQv})eE!H?Xyp2VVRS-&vX=fL$8>|H|au^h5-7?17-Rc6E_97_R=fbvY-3Z!@iIyLi z`*sunO%PQai5YX9Hv!LIWkZEflWrp`{z%37?H=1l;ctXOM>^N zUtCBM{DJ#P-=#c>~3CoM?eNVSFp zJ7t-ORq=-xdtNjxf7bI{2WWk;3Di1OD&fg~cZ@M0hC` z@+ky2X+W@a=wB+5Jp2wUBP`AQzCNy z8LJfqKUrc>rRm4+hxjA35&a%8i{$r=@Qojt z7VEq1?!eQr~^P}aO_m;{K* zmomVfOAeN2aegZS$ZqVU7Hs;$!t`O~YfZ|dsII1fqWGzD$%Qfp(-HF6Km`9CfIV^$kqed0f#=iz2|w$(h4>9AT%_d*cY2do`zYcW@1 zOl<%|%o$`Z*`U$iaP!u|aRGA5S0JZ=I1)dg{d1JrMAb3Ywe(pRCA3~x;PtzzLrF0i=dp4$>PK?%&LJB(L*A%%zn zD6UgM(j24~E*H__a+RM(1sa&=yiJu>LnjN?u7gD@Pa2q~K05tAg+8#NvK~X^HC`jj z#(Uqnf7Yt3vcXP{(FR-w%(=^9{Dx$_Q}{ejVV$Z9stETMX{Q<#!t7#G56cc8%}f1( z5I_udzBD>2Ti`iQ@w<-DN6#Iv7_rfiFRb(Nes@)kpEy6iuYL7HIB-$HvwsbC_37uJ zrw^_4-w4;%_+}TYSV+NC<>ck=Vch0wE!ZeuUk87Hf4K|p$g#K)N#sn&QIQ&&KDh(G z;~bdL?foly-aF#$&+q!hL>kdk@u5i(aWKq{Ko5Q`O3%;NzX`;tNL24;N5&?8I2Du^dT%)chFb9+LM>(Z?l$CWb{PDQ{is30oN zoF9b6{~EhQMJ~hJk!t&5n`W+Z@OcyFOlhU_+q8x_iI;v`;x5(O{b+ z;+A&0oe@)eq5G8G!d;k^b567Dv-09*v5EiYn%ClWQPcOBkPAiUqL*u+j4bKeP|a))HS(% z^mBj{cvpvd|F~tgO8rGVZ7=juiS}~<p%VisL{dW1MtDJLjjxE+Ae6ubkeGvZ!M zq;$X3e*olB&C~~g;Uh1tHxlZY#_14ANgnV&I?{_L-vA51(>c%siHfH`cEt#= zRfhL?X-;@c-2N_;$;aE`O2}A~Yiiu8bZx#<kz~0@sxH^h)QX^6EGokt< zochQkKx({Ic~)hyQhO5^Kj@757@pV=;-5x*g2~5SEX$7MMUPv``vHr6LNP*p*_)OO z0@4#s3?h77;+z}7sG+5akZ287OW%Jx`ny>FL!MIBcWzL=Muc?=nCYb9N{k#={A31h;jLTYH0Za*Zn1Jf|FYJQ1I;jc?#y@RljTq(0Ad#Bvm5wsRbHAD(9C` z1_^XuIOVY7egSIPopRF7N)l7wd1}7u2v1&j%Ae55NYX7IR8>D#kZ%@|UjDn-1h=KG z8pHV8v)h{H(7N*coxJ?f4c|N0E{0S4QEUl>cR4j>#6SMryD!c-);Q~bjB$WDW zEbzK&$1GxOE;M*&7qX!FG*1qqrcSG(OH-2kz}O1 zq1S+S$<<=QfA`N2;GZ%@nh&6?0e*2e%Siypv;MrxK{+$+h7HW3LAthIv=&2MZM@&w z#-{sW$G_@%Lu~UEo4wL_H-DoU{bK6)#r5(RCAsE!8i5D z#qx-jlYB1!V&b8B^{voN@vj2j1|WU z)cF4-3Ak%>>h6ON1EjUK**x?w@#F0DY<*Q}Xs6kbe6J_2u&fN+5m;>^wwcVF@mmfN zJ+EG}`L;Sp&!Znr*3d#bO8WdhHJ`^*4bC*5RdX~20fflrIJ!*L``N8zU4lOZ)U+Qh z{RBQc7d{3ZPCwa!(Q0Ot>kXqBe*|CVt^tQ|5C*Qn?TuFSKkgi$Qf;oS=m>{U_C@>} ztTSm&*vaZ#v~W&DFsB~F>I7-ULt1~3mCH2l_Mj(`MwIvAbR4O1knZpFphJ;-ETBZT zm4h#$gpvYU;g~xOAjMhwYN7xilqwYG;5=$yetB12LWPsxv#N^!7;wcnlH4h**#3c$ zM?mri(;3j`j{Eb6c`|hSTI|=7qMNh0ft|1TqOVbc&E_VsY#8(Y0_-2kd^bN*W?q^z zW|_=}uOq+G@)2aO5=0oU93Uv|9Upct&TJ|$xg%5oZ_0gCZZPquo#YdT5eP%qyzaQQ zfyH~qSSluzG);bK>dg`Gj9DBm5f|p>{Qai@=`XARic22aXF8n2)qaH0>b`6^IufU6 zcJa9@t0EIp1;zynEy^V=czY0c99#<-TnSY78*P@s=@zLjV3zZPxU7pu<)g=AUbTMm z=AFhgEGwHh^o3IWZ+=riebf~Dt)WkvokzQp5v7L}U39LK9eH8YoN8PBhg6QXzkJ;X ztGJvWvIq(vM6qWP zuExK{vED)^R1AtALX%K&h`rXXn%w@tEhot-f4c(k;1q6jO+G2Hl--@P)yU*3N5t9GHQ;7yRi*t zrAar;;%}c7*lE9K^1bzXIZ81PQK;^b;ya*tm=qovj`XbQ#vv_yrdmg;Z(bl%mWzwP zS}9`XfGa;rHl^#i0xpVu#Jh(&;jeceCaY*JN9C*e(*s zD41P|zk=+K{lP08HmkzA$*bU*rrI$iMS z-1!{El+)V`fOH{|?3!eSmTGU_E?=>)b8jPHCf{#h@e$w&0_6j)Gl{GF&5~K6c3pW? zbk=8Dn}?A?uRhK=R+}x}buBWLm){mt^AIYIJ&RgFN4;$2%GIF47^rb#s7f2H4Ik>k zv|krRLZum_FC~K=Y%yOLkI#U~+&PkaY>z8~(coHnUk7ulyKnNX_iCT*J(yuXAlh2VYC+8*FY}NgT+&CICqO#p{41Yo-+= z%xBI@yJNs=z-Ab7W|4)+=U8KC#Yv=P%;C7dSS&OjzeOQV(J{FZ)@fX^*J0VD?oLpU z($N#QWuRSih^XL*rR}jHCH4O>z?|D9UY?B7(zpe6Z7T5;IWCi}33)4<-5rFP$jT#~ zMCY@nZ+&68!qt`g2;*v7*rIuXa%b?n48w;*5PY^Pj#U@&6!s&ORhp5#xj%C2MKa@v zOKQ~L2IbM{Cti3ilVWOP{W(>T^LZfIEa;7eb&xEsRJnF=S_g$M2+0*#Ii2_^T31fw zUwrx`F;I}Vf)+OG`0U4 z<^_ZbZPRP;Y)J_}p)<@%k`}o8%*jP2k*v*9=Tu!;T%C@iv5(|wF#%HSjVpIyI8ne> zd2vDw5Xrv-<+UIs6_h8w!<1#aP~54#9A}NbbyGM0go@zGqh!A`wc%==R&?J&yYBbExvlM!leXfS`m3DLJ z^ami8Vn1(sg6ZF+gUlbAJlI4(ce2~MHn~(XT&h$uN-ar7P*Dn?08H9HWZ#_1V5zkr z90|pwfKtfyVm2P7Et@EwoFwF^F{%5lwLrhdghqwhR3zo^U5x(>YWVGBYYL?nyp7La+ zC0CAjEPToqK2i>y;F)XMEev6*d7a>HAERS{Py490;%;kYK=tL7P0&afdDMr%Cas@7 zJ57dNw{u$01z5$@k46VjwsRVd@Sa^)QL9<-h7EUsiOl{V%Si3f=P*4WADPB{aze)o zMoYn6v1|c~Y zS%J96ZT?Qa7RHWt0Va31&GOs{@|8r<+?qehA240+JH`wjlr*v22w|mS(VIHo18@}(*Fgn+#4h3ELi6#23bj#^NE)N z7sw?MKrX3e9wU}`N8Y4)fkRWn*@Ir~{PIm>;VZzvWP^VAl`Yt3Z4!f-_8BN+w2jPD zihA?HP#+Yr~b<2ibPVbPq zb58hwL>Pf0PjX z6*xi@0iEU~tbLG#s5vj7_ z^6G>_>3hA{pqE_QE;v_Y*_%7idNy=3YA19L#2TieF+*2@0{Fh_l#t_as5153m$mvr zG~Xb+o4(;cDuY3(5Ez`YcW~dAeIE#n5ST?Kl4;PrurRn~spRB=HoTRVlawy{R))AF zNud!|kWkY0x+D`V1$zz9jUMt)O6n;5G%5y6^CjbK{wjnxzGVXnfb$-XWWZ1d3rL@T zlvn80M$umXOqF%K_~9ReVvu}cc(%&(8~;6mv72@FC9GsW!hEhS?o-b3zX zy~_E7_RmCz{z6WMnQY+ATCEB*=Py)E4I|?%om@%ax(UK+{|*)Ao_WjTHsIXYXJ$|E z_3`%L!R1xx!F#RZdRS8?2uE`>G@RC=fdI^7n|3*IE+gdiVV^QtnyX->DVSSfSqm_W zjav_3qxTm9;us<^SW*X~@DsLJQT8h-ohMNDgA$uL*+Ny2t<;1x`{>*0|4c(v4&5@8 zmd_ZHwQwW~cdqG8V_i0 z>{v8;E*tLcXXa$klTE#RNx~JNS>JuVDgkTNijhHrKKc^ki<`w6=A7Xo$DX$Ef~giwMZK2%BuY6ocYJ$a=M~= zGAz?%rdMcTEq#m^CG5j`PZk+U*LJaV(rlqf^eU#<8jvMa$N+rm2L?sj?~`iE9!NK{ z2j>KWhQ|5r^J^zm1=?dAaT)_04V}M~#=_7k`d)rfFBv?dq3Xp7iBb(DMj=#y^P?8zr{&_~7RRM=>`(zN;r z)i6yiin>}I9Ei8>&l}{vj!zH}P2Gn_Gd0GgYe0xQ=Ra7^3f}3QUn%nPn`4skpze2V zYp(h^>xNdck4lkA=B>IV67I`n%&HiG1_C&b+7w5vdd*KLLn{Z2{q#wj8ms))R%a|2>6lKTG zH$tg15S~3L?V~v{Yr_(AN76 zbI!E-ahG*EXv_(Fcf;YlKy1^QU`Rzuiu65!zxeG0ghML+?HjBL0Vc2dw~94n&S6%h zhS&xq)Y;*H^s^I<+rW(!HUGIvSydTEnNnBbsky0Bbr%75Rr(R%jcfm|pStQT8}st* zlOJ-8KC=Y8cZ-!tX*+QVdvZ~{s@}^4xN8QPFQ^UOP_^^f1vqt9GS6*HkMsYYSBU(R zxqfjp%zQ)DIDCL7BQJ5rZX9cjv}9g_KPt_3_YFCd%oN?S-2aV_=nTzul%afhwZ(B0 zdMhw2VBP;@xiQkiNVvoQ*<-ZH2+3F_MKO7DM-FOov(fT96ASMB*}B}h?Ch@;iR-|N z^&-_f-`jtvd8b_&tIBN&CYy)$a^$SpXeC1e0V>U3etnATk5RNTQ0&l8fR`&4y!Jt7 z<6}YO4jTRTPB^AIn;F(2U7HuuKWMEippOp@{n>^Z-^fzkWkDhKRp`Bn{}Aok?NYH! ztO0+c`Vd(_OZ(6=nE&IcU9D_sUW>KJGXXjBDJ*LC*{>}=KZjN%Z}b9BRURSt6C4^| zrC^@o=v&#lP!HEt9*GW!Jg8)78IF4>-TxEdM~o2h-^7 zqP~R-`QD|*Bd>LDbow$FzX968Iij)C-!H+>IsEy$$@G0z>Bs5wyCXLU_>?erFf=7> zX+fF%uHn$G;e@Nx@%^K$X6?B?tt7~4Ze*})z3XC<^4~vEpl<%9IjkOW4{kFk!}Fx9 zcV<)_YcC9sk?}vqS^I(JgVN=m+`T5~GT8hzi)@>UYpe51e!$QgNYeZ6tj#1>zNPm$ zpWlhdx84FSQLqDhgqlAo_q@e={qIG=E6c{I1A6{XbL51C-f1`7qs~NjgWNNzr(v>^ zgkuqpXbp?bo*f3EK;k?d-o&7LRhG~}JZG}IxtKF{ox}36@jc1O5s-!KEvc0G+(Cid z8VHs((sH${!Tjb4dKSLnO>;YP=MIXa87xur&NGt-_p?YyQP9%?pwZy~kj3>%$@ z)9DS@u9KwuxPPjhirwke+R}~#IXubp!^T6PR3qIf8b4`o3)*WZX8&`&efNrZy>&6{ zkOF-=5%ED^6+svt1|NyqsIf_7sgeAJJw=@Yo9d$#JCqraGER<9UW=l)SE(~>l9ufr z{D6IR9NCkEz|39~@4A>CFu2Sy{r3kxSeOesIjBEvz~^!Gf@+I}FNwAF3*Pt#{mI`* zzuj^wPo_R-t=;qIXsFE-CE=1?^QGjeXxr+pYveWUMi-kS@ zCh`x-;BfO2t?)#?=2+ZpRm-{2O5eonU7ZD?m#jDI-+=|=i_|0joL@u2iYp)0EF|Dn zrS7RZ+pv9>9cmO%SF+P|;8Xg{;19`NG)5-xC_DQ%i_X}Mbbcn2M6~t|OS2_q3Y8)& zQ&K{A9-XnL_Gg&zGuC~ueq(UvUW(JQoyrqpR1%*-$3;`S@D>dEC5T`EZA=t|Miv!9 zFDUtV|K{4^^vYl;|Ag_LjVkq*xJ~R?i`BqcNLA*G&23ar?oO~zC7N~H<*D32X>p`G z^`Ae;x^;O*UK;TDY{Yu}nuJTD>JMG$hmG-lHcCMw#+=szYe`8$I5)pH96K?D(iuin z+6Be~$mZXAtLO^R#d3A5Qd>{WSt*1JX%dc1R5Mogt=B{J`}X!*I;L-|1^!+Wl(4~- z%vl}Kw|9IP*(sxFWFF^7T`jg2@m1r5c++1Q-l4ljCS?Y!&uWxjs_&wug!iw5x=Yx{ z_XbHD^49ohe0M7Feecf}+zfa}c43ME_o?+i3YY)WwUO$-MhB)>ebQOFcpWzo8W<~ux;o~EQZ=0-6h>E&0 zH+^X-Juw0f9Ra=huaQQ07;-Xwkp7AI4dOoFtG_QGh5NfNh+9Us=7#FmAiR4FlLyk( z5GBn1;=Zfkn1coeHOB9RX7nXj)a0Ako~{)JxlGsaBV&&Q)_$k($N1dI#NC-1G5#q8 z{ToGcq*8bZ7$fJB>-~2vW%2vRx=LK_<%x33D%Rr)UlMYR4lK&1B`&({P z>m0!j;%M-#Q&$jg^7O&fT>`&1fBus9qVXzz-l%!dy7bS(AVZw^FHZl*1-SLC2Z{Ky zHrDPCNly%eKinT3yev#Rv;N+TiHDkm!{89A`y%akIjFB;XIJ|3-;VawkaN`$CM!YY zk)g;~9pK?D&5oOj60ep~)pS-LSA>o4shkSY#>H75L_Mm|O(6nF+RM>)p2ZE2=Fk{F zt&H{>e#8AnG_inhZw#4g$xLy0cNDE)T9n;bfz&)W9M!=>n`S3xya5;V{?V08KdzH7&G zMc}thgZN;y&Nj9jE>e8z7%4ZUP+ zxOdelYoHP546Ex$0^^B=4c%U#VFd|#rSXI0@|Ubf{u!q=D}PMVC)uxMkDr%MLuCZ( zgJkgp6(la?_;31_1JQ=1r%R#uBqa|CkcW-9HVGG?GsPK><;Nq8EC>dbN%_P7!i6@sXScqB-#>l6sy#F$Q_;SVyvjn)|hva=3JZEUefk0Av_~krqCet{PJ(hk0DAY z`uivdl@+eO>Ex=_(?yLUx>>_hCi-Hpo~Vro#&e5ePq3+PA^(1>A%jF$%EoR3j7>zH zrJ0lM2+toq=ev5W-FBTsc+w+pP#md1t_&!HDbBQ(S7PcfE8{y%3HszY`_yfZJz=C` zZ7p+O<-u|H`%qr70F?iN{NXB|m=P}%e#fdQ`A?|_Omp7|&lASqwPIJAu%C+m4v~Du zgU8%l8`)*Ysr&1wqI2u6a1r;Jg}s*OjQrCo$&oh7M0&xjEr8XE>)h_H`Xm|7_4s_` zlRM?eFM(*^#zNfA-(KyX9~m$ZU|Ep-r6#m+N*$tmd|LZ|j~28fd2Kyr0jIY_Ar~fg zV))N9RSko#F-7<*%}glnpMPw%jxLE5n#`TVQ6%rS3Ri1-i%@dE*T7D(ZuaQ;SGxv! zr5}kn^iz6fXn954SyZ#!*&_sK6dA8GiRAv1u#v#1)S#@G^FuSwi&eSZSeebIN#g`Pv^Pvo;>oQ>u84yDn$1kM+VeVzJ_ z;$Yh>xqyfEyN@6Mg0xJEcD{70yZxc!3)U~2GDwBOE!8jKxZN_eGCjw*0Rst6toSkf zfQgr#XLz#DLcRToIU#r0q}oFyAiVIzwVW}qt$F97Z6TevyvtMZck5mqQ3?zM-Q-x^ z+x}J7zKuvGhUXUp)03vXygYnyKX-U0K6gso*4~^T*JkL45@)^B3(7wrq<_Rd>sH$c zWKC}omVf1JuGYiM9t>QooFX}17Ju&WA0pZf{Kb<^46qxYJ7NB+O57*S_EQV{r^%8b zL7el*BFmacF|%Xnn?`PHhP1NfQ_>R8#}U9ZBZ)bbs`W0y?oWL`^KnvzxCeiuuo806 z!Wod{9La4mh**!j2|N;fme*-U0b;1ApFREaI%%9G6q8H%7d(6qh_s;uDZ8rxJXuMa zJ7s-y6I8V-d&83dJ%Lmb=u{b}!M*T#f78)}G z4Ndt_Tdzf~H8aUWRbE3er)gu>z-P9CXf!r7L@C?(dk+ zx&0vi_cY2GR(!~OmS7q2h^bEjcj>vvj5BWX4Q zy|+(k0TBAF90Q`8@5x}v(_4yG%COx*_OS+D@-fJ>VESER;9GvgGrVlG?Pf?}E71)x zMbA%#a+o~YXZxt&YTm*DSPVw$fAju%Rdgo-`xHB3L^Gvy%NdcwcVN2ldM zDZn%j`}Hw7@n#RRJhcp+glBZAFk#t8S<=TIUNze%gQn~eC%1)b@Y!7B zRbt3>%8_TC2?!T3Envw}xX0?CfR}5wv zPiycoCOr-h;>vzq+?KGhrXNhmh?)O4Nq0)3ZNcm=++6M@*$k%JjAtJBfYUTVMvjA< z$$B{9&c`AYk9Jj%W_=gwd6HGI{Qr1*%b>WrU~3o&;R%}H?!jGxd$3@^9fJD+12agl zKyVn`-66QkgkZrvxVu|$3GyE9z2EnXDhjI3KHab2JH!?4^s>~^?E5AQu%LO`F< zu$THw`s1(>8UCy4f;{Ii1*;u6R$U8tAzX>h2{XqsUNS@j`K>X=>P<55h>fQ3!Dhic>Bvo}+@`%3RqjHeuo;a+H$#t!dwQl#a9Z92X_CfJ=4Y`=j| zQNG&0t%92p46CXR&aTfnN{H3lv%ZcZ*`G1mzh!Id2)s%!f7nEykId!R>)1bfY-0RA z6>HSb`lhC*PB9l{`VqlbHe#sbu&RBvf`J)$-vM#;<%#BE@nYRiDu%v7RF1Qry~!CG zz1om34n7RrQwz&Rou6eYIn&RsQvILEuPH54QQYYQ70M5S7dC|=KB1{Q?IIrL(*`bn z0_xvD42B42hsp@;?!$~VWE<&v!@{$Z6Q!{U11Q(YU2@m!831d|RsX8NK-SLazp#9v z7`?SBjfa4DzUs!)`DmlMnJ6gq zA)fqK7edIPl7qcFJVW)^;lc4^(nnAy1tPg8sZ%s_A)K0oITPKjM~d*k@iVSm`K%VJ zXkW{;Wb&t=qS7q&zNdOhNVCYXiIGPhP!6dWRTT{ccX j>K#%Tm|BN8TJ*e!m-Wm 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 B1$O`d literal 0 HcmV?d00001 diff --git a/build/pack-repo.ps1 b/build/pack-repo.ps1 new file mode 100644 index 0000000..d5f84b3 --- /dev/null +++ b/build/pack-repo.ps1 @@ -0,0 +1,270 @@ +Param +( + [Parameter(Mandatory = $false)] + [string]$ownVersion, + [Parameter(Mandatory = $false)] + [bool]$runTest +); + +$assemblyPath = "..\src\shared\GeneralAssemblyInfo.cs"; +$defaultVersion = "1.0.0.0"; +$nugetPath = "../nuget"; +$data = ("..\src\MLogHelperDotNet\MLogHelperDotNet.csproj"); +$testExec = $false; + +<# + .SYNOPSIS + A brief description of the Get-CurrentAssemblyVersion function. + + .DESCRIPTION + Get current assembly version + + .EXAMPLE + PS C:\> Get-CurrentAssemblyVersion + + .NOTES + Additional information about the function. +#> +function Get-CurrentAssemblyVersion +{ + [OutputType([string])] + param () + + $assemblyInfo = (Get-Content $assemblyPath); + $asVersion = ($assemblyInfo -match 'AssemblyVersion\(".*"\)'); + $asVersion = $asVersion -split ('"'); + $asVersion = $asVersion[1]; + + return $asVersion; +} + +<# + .SYNOPSIS + A brief description of the Build-And-Pack-BuildPack function. + + .DESCRIPTION + Build project and pack as package (u pkg) + + .PARAMETER packVersion + Package/Build version + + .PARAMETER currentVersion + A description of the currentVersion parameter. + + .EXAMPLE + PS C:\> Build-And-Pack-BuildPack -packVersion 'Value1' + + .NOTES + Additional information about the function. +#> +function Set-BuildAndPack +{ + [CmdletBinding()] + [OutputType([bool])] + param + ( + [Parameter(Mandatory = $true)] + [string]$packVersion, + [string]$currentVersion + ) + + try + { + Write-Host "Project restore '$($_)'!" -ForegroundColor Green; + dotnet restore $($_); + + Write-Host "Build in Release '$($_)'!" -ForegroundColor Green; + $buildResult = dotnet build $($_) --source https://api.nuget.org/v3/index.json -c Release /p:AssemblyVersion=$packVersion /p:AssemblyFileVersion=$packVersion /p:AssemblyInformationalVersion=$packVersion; + if ($LASTEXITCODE -ne 0) + { + Set-VersionAssembly -packVersion $currentVersion; + Write-Host $buildResult; + + return $false; + } + + Write-Host "Pack in Release '$($_)'!" -ForegroundColor Green; + $packResult = dotnet pack $($_) -p:PackageVersion=$packVersion --no-build -c Release --output $nugetPath; + if ($LASTEXITCODE -ne 0) + { + Set-VersionAssembly -packVersion $currentVersion; + Write-Host $buildResult; + + return $false; + } + + return $true; + } + catch + { + Write-Host -foregroundcolor Red "An error occurred: $_" + + return $false; + } +} + +<# + .SYNOPSIS + A brief description of the Get-TimeStamp function. + + .DESCRIPTION + Get time stamp version + + .EXAMPLE + PS C:\> Get-TimeStamp + + .NOTES + Additional information about the function. +#> +function Get-TimeStamp +{ + [CmdletBinding()] + [OutputType([int])] + param () + + $current = [System.DateTime]::Now; + $end = [System.DateTime]::Now.Date; + $diff = (New-TimeSpan -Start $current -End $end).TotalSeconds / 10; + $timeSec = If ($diff -le 0) { $diff * -1 } + Else { $diff }; + + return [int]$timeSec; +} + +<# + .SYNOPSIS + A brief description of the Set-VersionAssembly function. + + .DESCRIPTION + Set current version in assembly file + + .PARAMETER packVersion + A description of the packVersion parameter. + + .EXAMPLE + PS C:\> Set-VersionAssembly -packVersion 'Value1' + + .NOTES + Additional information about the function. +#> +function Set-VersionAssembly +{ + [CmdletBinding()] + [OutputType([void])] + param + ( + [Parameter(Mandatory = $true)] + [string]$packVersion + ) + $NewVersion = 'AssemblyVersion("' + $packVersion + '")'; + $NewFileVersion = 'AssemblyFileVersion("' + $packVersion + '")'; + $NewAssemblyInformationalVersion = 'AssemblyInformationalVersion("' + $packVersion + '")'; + + (Get-Content $assemblyPath -encoding utf8) | + %{ $_ -replace 'AssemblyVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)', $NewVersion } | + %{ $_ -replace 'AssemblyFileVersion\("[0-9]+(\.([0-9]+|\*)){1,3}"\)', $NewFileVersion } | + %{ $_ -replace 'AssemblyInformationalVersion\("[0-9x]+(\.([0-9x]+|\*)){1,3}"\)', $NewAssemblyInformationalVersion } | + Set-Content $assemblyPath -encoding utf8 +} + +<# + .SYNOPSIS + A brief description of the Exec-TestSolution function. + + .DESCRIPTION + Execute solution test + + .EXAMPLE + PS C:\> Exec-TestSolution + + .NOTES + Additional information about the function. +#> +function Exec-TestSolution +{ + [CmdletBinding()] + [OutputType([bool])] + param () + + # Merge all streams into stdout + #$result = dotnet test "..\src\tests\*.csproj" *>&1 + + #No test + return $true; + + # Evaluate success/failure + if ($LASTEXITCODE -eq 0) + { + return $true; + } + else + { + $errorString = $result -join [System.Environment]::NewLine; + Write-Host -foregroundcolor Red "An error occurred: $errorString"; + + return $false; + } +} + +If ($runTest -eq $true) +{ + Write-Host "Init test solution...`n" -ForegroundColor Green; + $testExec = Exec-TestSolution; +} +Else { $testExec = $true; } + +If ($testExec -eq $true) +{ + Write-Host "Path to pack: '$nugetPath'`n" -ForegroundColor Green; + + $currentVersion = ""; + If ($ownVersion -eq $null -or $ownVersion -eq "") { $currentVersion = Get-CurrentAssemblyVersion; } + Else { $currentVersion = $ownVersion; } + + $directoryInfo = Get-ChildItem $nugetPath | Where-Object { $_.Name -match '[a-z]*.1.0.0.nupkg$' } | Measure-Object; + If ($defaultVersion -eq $currentVersion -and $directoryInfo.count -eq 0) + { + Set-VersionAssembly -packVersion $currentVersion; + + $data | ForEach-Object { + $buildResult = Set-BuildAndPack -packVersion $currentVersion; + If ($buildResult -eq $false -or $buildResult -contains $false) + { + Write-Host "`nBuild/pack failed!!!" -ForegroundColor Red; + + exit; + } + } + + Write-Host "`nPack executed with success with version: $currentVersion!" -ForegroundColor Green; + + exit; + } + Else + { + $finalVersion = ""; + If ($ownVersion -eq $null -or $ownVersion -eq "") + { + $versArray = $currentVersion.Split('.'); + $finalVersion = $versArray[0].ToString() + "." + $versArray[1].ToString() + "." + (([int]$versArray[2]) + 1).ToString() + "." + (Get-TimeStamp).ToString(); + } + Else { $finalVersion = $ownVersion; } + + Set-VersionAssembly -packVersion $finalVersion; + + $data | ForEach-Object { + $buildResult = Set-BuildAndPack -packVersion $finalVersion -currentVersion $currentVersion; + If ($buildResult -eq $false -or $buildResult -contains $false) + { + Write-Host "`nBuild/pack failed!!!" -ForegroundColor Red; + + exit; + } + } + + Write-Host "`nPack executed with success with version: $finalVersion!" -ForegroundColor Green; + + exit; + } +} +Else { exit; } \ No newline at end of file diff --git a/build/pack.ps1 b/build/pack.ps1 new file mode 100644 index 0000000..cdfdf09 --- /dev/null +++ b/build/pack.ps1 @@ -0,0 +1,21 @@ +$asCfg = $args[0] +if ($asCfg -eq 'Release') +{ + $assemblyInfo = (Get-Content ..\shared\GeneralAssemblyInfo.cs); + $finalVerion = '1.0.0.0'; + $nugetPath = "../../nuget" + + $assemblyInfoVerion = ($assemblyInfo -match 'AssemblyInformationalVersion') + $assemblyInfoVerion = $assemblyInfoVerion -split ('"') + $assemblyInfoVerion = $assemblyInfoVerion[1] + + $assemblyVersion = ($assemblyInfo -match 'AssemblyVersion\(".*"\)') + $assemblyVersion = $assemblyVersion -split ('"') + $assemblyVersion = $assemblyVersion[1] + + if ($assemblyInfoVerion -eq ' ' -or $assemblyInfoVerion -eq '' -or $assemblyInfoVerion.EndsWith(".*")) { $finalVerion = $assemblyVersion; } + else { $finalVerion = $assemblyVersion + '-' + $assemblyInfoVerion; } + + dotnet pack -p:PackageVersion=$finalVerion --no-build -c Release --output $nugetPath +} +else { Write-Host "Solution not in 'Release' mode. Received:" $asCfg } diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/branch-guide.md b/docs/branch-guide.md new file mode 100644 index 0000000..ff34d67 --- /dev/null +++ b/docs/branch-guide.md @@ -0,0 +1,14 @@ +# BRANCH GUIDE + +Following are the most important branches: + +- `develop`: Contains the latest code **and it is the branch actively developed**. Note that **all PRs must be against `develop` branch to be considered**. + +- `main`: Synced time to time from develop. It contains "stable" code, although not the latest one. The plan to do periodic merges from `develop` to `main`, but not right now. + +Any other branch is considered temporary and could be deleted at any time. Do not do any PR to them! +To make new features, you branch off of `develop` into feature branches i.e. `feature/my-new-feature`. To prep a new release for deployment, you make a release branch off of develop i.e. `release/1.4.9`, which you then merge to main. + +To do a hotfix on production/main, you make a hotfix branch off of main, i.e. `hotfix/fix-prod-bug`, which you merge back into main. + +Thanks! diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..1f7b255 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,108 @@ +> :point_up: From the beginning let's make clear one moment. It is a wrapper for logging service provided by [e-governance agency](https://egov.md/). If you accessed this repository and already installed it or you want or install the package you already contacted the agency and discussed that and obtained the necessary information for clean implementation. :muscle: + +
+ +Currently is available only `REST` logging implementation. `SOAP` implementation will be added in the future. + +In curret wrapper are available a few methods like: +* `RegisterEvent/Async`; +* `RegisterEventBatch/Async`; + +For more details, please check the documentation obtained from the responsible authorities where you can find all the smallest details necessary for the implementation and understanding of the working flow. +
+In dependece of what type of project you have, in you configuration file please provide available configurable parameters.
+ +


+ +**Configure the application settings file** + +In case you use `netstandard2.0`, `netstandard2.1`, `net5`, `netcoreapp3.1` in your project find a settings file like `appsettings.json` or `appsettings.env.json` and complete it with the following parameters. +```json + "RemoteMLogOptions": { + "ServiceClientAddress": "logging service", + "ServiceCertificatePath": "service certificate", + "ServiceCertificatePassword": "password for service certificate", + "ServiceEventRegisterPath": "registration log path", + "ServiceEventGetPath": "query log path", + "ServiceTimeoutInMinute": "request execution timeout" + } +``` + +If you have the `app/web.config` file (must common for .net framework projects) +```xml + +
+ + + + + + +``` + +
+ +**Calling the service** + +In case of using the `netstandard2.0+` in your project, after adding configuration data, you must set dependency injection for using functionality. In your project in the file `Startup.cs` add the following part of the code: +```csharp +public void ConfigureServices(IServiceCollection services) + { + ... + + services.AddMLogService(Configuration); + + ... + } +``` + +In the service that will be implemented, inject internal service (`IMLogEndpointClientService`). +```csharp +public class Logging + { + private readonly Func _clientFactory; + + public Logging(Func clientFactory) + { + _clientFactory = clientFactory; + } + + public IActionResult SendSoap() + { + var endpoint = _clientFactory(EndpointType.REST); + var result = endpoint.RegisterEvent("JSON content"); + + return JsonResult(result); + } + ... + } +``` + +**Net Framework**
+```csharp +var logInstance = new MLogRestClientService(); +var log = logInstance.RegisterEvent(new MLogEventDto() + { + EventCorrelation = Guid.NewGuid().ToString(), + EventID = Guid.NewGuid().ToString(), + EventTime = DateTime.Now, + Subject = "test" + }); + + //---------------------------------------- + var log = logInstance.RegisterEvent("JSON content"); +``` + + +For more information about the fields and how to complete them, you can find in integration guide obtained from the service provider. + + + + diff --git a/src/MLogHelperDotNet/.editorconfig b/src/MLogHelperDotNet/.editorconfig new file mode 100644 index 0000000..96b5a38 --- /dev/null +++ b/src/MLogHelperDotNet/.editorconfig @@ -0,0 +1,250 @@ +root = true +# Remove the line below if you want to inherit .editorconfig settings from higher directories +[*.json] + +indent_size = 4 +indent_style = space +tab_width = 4 + +[*.config] + + +# C# files +[*.cs] + +#### Core EditorConfig Options #### +charset = utf-8-bom + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false + +# this. and Me. preferences +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:none +dotnet_style_predefined_type_for_member_access = true:none + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:error +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:error +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true:error + +# Parameter preferences +dotnet_code_quality_unused_parameters = all + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_constructors = true:suggestion +csharp_style_expression_bodied_indexers = true:suggestion +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = true:suggestion +csharp_style_expression_bodied_operators = true:suggestion +csharp_style_expression_bodied_properties = true:suggestion + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +#csharp_preferred_modifier_order = +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:warning + +# Code-block preferences +csharp_prefer_braces = when_multiline:suggestion +csharp_prefer_simple_using_statement = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_range_operator = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +# csharp_indent_labels = flush_left +csharp_indent_labels = no_change +# csharp_indent_switch_labels = false +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +# csharp_space_around_declaration_statements = do_not_ignore +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Naming styles #### + +# Naming rules + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Symbol specifications + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Naming styles + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case + +# Instance fields are camelCase and start with _ +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = silent +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style + +dotnet_naming_symbols.instance_fields.applicable_kinds = field + +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ + +# ReSharper properties +max_line_length = 200 +resharper_csharp_max_line_length = 200 +resharper_csharp_wrap_after_declaration_lpar = true +resharper_csharp_wrap_extends_list_style = chop_if_long +resharper_csharp_wrap_lines = false +resharper_max_attribute_length_for_same_line = 300 +resharper_place_type_constraints_on_same_line = true +resharper_wrap_before_arrow_with_expressions = true +resharper_wrap_before_extends_colon = false + + +[*.xml] +indent_style=space + +[Reference.cs] +dotnet_diagnostic.cs1591.severity=silent + +[MNotify.svc] +dotnet_analyzer_diagnostic.severity=silent \ No newline at end of file diff --git a/src/MLogHelperDotNet/Abstractions/IMLogEndpointClientService.cs b/src/MLogHelperDotNet/Abstractions/IMLogEndpointClientService.cs new file mode 100644 index 0000000..684a4a6 --- /dev/null +++ b/src/MLogHelperDotNet/Abstractions/IMLogEndpointClientService.cs @@ -0,0 +1,90 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-07 00:36 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-13 15:13 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using AggregatedGenericResultMessage.Abstractions; +using MLogHelperDotNet.Models; +using System.Collections.Generic; +using System.Threading.Tasks; + +#endregion + +namespace MLogHelperDotNet.Abstractions +{ + ///------------------------------------------------------------------------------------------------- + /// Interface for im log endpoint client service. + /// + ///================================================================================================= + public interface IMLogEndpointClientService + { + ///------------------------------------------------------------------------------------------------- + /// Registers the event described by logEvent. + /// The JSON event. + /// An IResult<string> + ///================================================================================================= + IResult RegisterEvent(string jsonEvent); + + ///------------------------------------------------------------------------------------------------- + /// Registers the event batch described by logEvents. + /// The JSON events. + /// An IResult<string> + ///================================================================================================= + IResult RegisterEventBatch(IReadOnlyCollection jsonEvents); + + ///------------------------------------------------------------------------------------------------- + /// Registers the event asynchronous described by logEvent. + /// The JSON event. + /// The register event. + ///================================================================================================= + Task> RegisterEventAsync(string jsonEvent); + + ///------------------------------------------------------------------------------------------------- + /// Registers the event batch asynchronous described by logEvents. + /// The JSON events. + /// The register event batch. + ///================================================================================================= + Task> RegisterEventBatchAsync(IReadOnlyCollection jsonEvents); + + ///------------------------------------------------------------------------------------------------- + /// Registers the event described by logEvent. + /// The log event. + /// An IResult<string> + ///================================================================================================= + IResult RegisterEvent(MLogEventDto logEvent); + + ///------------------------------------------------------------------------------------------------- + /// Registers the event batch described by logEvents. + /// The log events. + /// An IResult<string> + ///================================================================================================= + IResult RegisterEventBatch(IEnumerable logEvents); + + ///------------------------------------------------------------------------------------------------- + /// Registers the event asynchronous described by logEvent. + /// The log event. + /// The register event. + ///================================================================================================= + Task> RegisterEventAsync(MLogEventDto logEvent); + + ///------------------------------------------------------------------------------------------------- + /// Registers the event batch asynchronous described by logEvents. + /// The log events. + /// The register event batch. + ///================================================================================================= + Task> RegisterEventBatchAsync(IEnumerable logEvents); + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Configurations/CertificateConverter.cs b/src/MLogHelperDotNet/Configurations/CertificateConverter.cs new file mode 100644 index 0000000..12bd397 --- /dev/null +++ b/src/MLogHelperDotNet/Configurations/CertificateConverter.cs @@ -0,0 +1,71 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-02-08 07:41 +// +// Last Modified By : RzR +// Last Modified On : 2023-02-10 04:17 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using DomainCommonExtensions.CommonExtensions; +using DomainCommonExtensions.DataTypeExtensions; +using System; +using System.ComponentModel; +using System.Globalization; +using System.Security.Cryptography.X509Certificates; + +#endregion + +namespace MLogHelperDotNet.Configurations +{ + /// + /// Certificate converter + /// + public class CertificateConverter : TypeConverter + { + /// + /// Register + /// + public static void Register() => TypeDescriptor.AddAttributes(typeof(X509Certificate2), + new TypeConverterAttribute(typeof(CertificateConverter))); + + /// + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + => sourceType == typeof(string) || base.CanConvertFrom(context, sourceType); + + /// + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + var text = value as string; + if (text.IsNull() || string.IsNullOrWhiteSpace(text)) + { + return base.ConvertFrom(context, culture, value); + } + + var num = text.IndexOf('|'); + if (num.IsLessZero()) + { + return CertificateLoader.Public(text); + } + + var text2 = text; + var num2 = num - 0; + var certificatePath = text2.Substring(0, num2); + var text3 = text; + var length = text3.Length; + num2 = num + 1; + var length2 = length - num2; + + return CertificateLoader.Private(certificatePath, text3.Substring(num2, length2)); + } + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Configurations/CertificateLoader.cs b/src/MLogHelperDotNet/Configurations/CertificateLoader.cs new file mode 100644 index 0000000..53b684b --- /dev/null +++ b/src/MLogHelperDotNet/Configurations/CertificateLoader.cs @@ -0,0 +1,229 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MNotifyHelperDotNet +// Author : RzR +// Created On : 2023-02-08 07:41 +// +// Last Modified By : RzR +// Last Modified On : 2023-02-10 04:17 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using DomainCommonExtensions.CommonExtensions; +using DomainCommonExtensions.DataTypeExtensions; +using MLogHelperDotNet.Extensions; +using System; +using System.IO; +using System.Security.Cryptography.X509Certificates; + +#endregion + +#if NETSTANDARD2_1_OR_GREATER || NET || NETCOREAPP3_1_OR_GREATER + +using System.Runtime.CompilerServices; +using System.Security.Cryptography; + +#endif + + +// ReSharper disable IdentifierTypo +// ReSharper disable ExpressionIsAlwaysNull +// ReSharper disable JoinDeclarationAndInitializer + +namespace MLogHelperDotNet.Configurations +{ + /// + /// Certificate loader + /// + /// + public static class CertificateLoader + { + /// + /// Load public certificate + /// + /// Certificate path + /// + /// + public static X509Certificate2 Public(string certificatePath) + { + if (string.IsNullOrWhiteSpace(certificatePath)) + return null; + + var certificate = LoadPublicCertificateFromChain(certificatePath); + return !certificate.IsNull() + ? certificate + : File.Exists(certificatePath) + ? new X509Certificate2(certificatePath) + : !Directory.Exists(certificatePath) + ? null + : LoadPublicCertificateFromChain(Path.Combine(certificatePath, "tls.crt")); + } + + /// + /// Load private certificate + /// + /// Required. Certificate path + /// Optional. Certificate password. The default value is null. + /// + /// + public static X509Certificate2 Private(string certificatePath, string certificatePassword = null) + { + X509Certificate2 result; + if (string.IsNullOrWhiteSpace(certificatePath)) + return null; + +#if NETSTANDARD2_1_OR_GREATER || NET + if (File.Exists(certificatePath) && !string.IsNullOrWhiteSpace(certificatePassword)) + { + var x509Certificate2Collection = new X509Certificate2Collection(); + x509Certificate2Collection.Import(certificatePath, certificatePassword, X509KeyStorageFlags.DefaultKeySet); + if (x509Certificate2Collection.Count > 1) + { + CertificateLoader.StoreIntermediateCertificates(x509Certificate2Collection, 0, x509Certificate2Collection.Count - 1); + } + var x509Certificate2Collection2 = x509Certificate2Collection; + var index = x509Certificate2Collection2.Count - 1; + + return x509Certificate2Collection2[index]; + } + + if (!Directory.Exists(certificatePath)) return null; + + var certificateFile = Path.Combine(certificatePath, "tls.crt"); + var path = Path.Combine(certificatePath, "tls.key"); + + if (!File.Exists(path)) return null; + + using var x509Certificate = CertificateLoader.LoadPublicCertificateFromChain(certificateFile); + if (x509Certificate == null) + { + result = null; + } + else + { + var array = File.ReadAllLines(path); + if (array.Length < 3) + { + result = null; + } + else + { + var array2 = + Convert.FromBase64String(string.Concat(RuntimeHelpers.GetSubArray(array, new Range(1, new Index(1, true))))); + if (array[0].Contains("EC PRIVATE KEY", StringComparison.OrdinalIgnoreCase)) + { + using var ecdsa = ECDsa.Create(); + if (!ecdsa.IsNull()) + { + ecdsa?.ImportECPrivateKey(array2, out _); + + return new X509Certificate2(x509Certificate.CopyWithPrivateKey(ecdsa) + .Export(X509ContentType.Pfx)); + } + } + if (array[0].Contains("RSA PRIVATE KEY", StringComparison.OrdinalIgnoreCase)) + { + using var rsa = RSA.Create(); + rsa.ImportRSAPrivateKey(array2, out _); + + return new X509Certificate2(x509Certificate.CopyWithPrivateKey(rsa).Export(X509ContentType.Pfx)); + } + if (array[0].Contains("DSA PRIVATE KEY", StringComparison.OrdinalIgnoreCase)) + { + using var dsa = DSA.Create(); + dsa.ImportPkcs8PrivateKey(array2, out _); + + return new X509Certificate2(x509Certificate.CopyWithPrivateKey(dsa).Export(X509ContentType.Pfx)); + } + result = null; + } + } + +#else + result = new X509Certificate2(certificatePath, certificatePassword); +#endif + + return result; + } + + /// + /// Load public certificate from chain + /// + /// Certificate file path + /// + /// + private static X509Certificate2 LoadPublicCertificateFromChain(string certificateFile) + { + if (!File.Exists(certificateFile)) + return null; + + var x509Certificate2Collection = LoadPublicCertificatesChain(certificateFile); + if (x509Certificate2Collection.Count.IsZero()) + return null; + + if (x509Certificate2Collection.Count > 1) + StoreIntermediateCertificates(x509Certificate2Collection, 1, x509Certificate2Collection.Count); + + return x509Certificate2Collection[0]; + } + + /// + /// Load public certificate from chain + /// + /// Certificate file path + /// + /// + private static X509Certificate2Collection LoadPublicCertificatesChain(string certificateFile) + { + var x509Certificate2Collection = new X509Certificate2Collection(); + var array = File.ReadAllLines(certificateFile); + var num = -1; + for (var i = 0; i < array.Length; i++) + { + if (num.IsLessZero()) + { + if (array[i].Contains("BEGIN CERTIFICATE", StringComparison.OrdinalIgnoreCase)) num = i + 1; + } + else if (array[i].Contains("END CERTIFICATE", StringComparison.OrdinalIgnoreCase)) + { + x509Certificate2Collection.Add( + new X509Certificate2(Convert.FromBase64String(string.Concat(array.SubArray(num, i - 1))))); + num = -1; + } + } + + return x509Certificate2Collection; + } + + /// + /// Load certificate from store + /// + /// Certificate collection + /// Start index + /// End index + /// + private static void StoreIntermediateCertificates(X509Certificate2Collection certificates, int start, int end) + { + if (start >= end) + return; + + var x509Store = new X509Store(StoreName.CertificateAuthority, StoreLocation.CurrentUser); + x509Store.Open(OpenFlags.ReadWrite); + for (var i = start; i < end; i++) + { + var certificate = certificates[i]; + if (certificate.Issuer != certificate.Subject && !x509Store.Certificates.Contains(certificate)) + x509Store.Add(certificate); + } + + x509Store.Close(); + } + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptions.cs b/src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptions.cs new file mode 100644 index 0000000..b5014e5 --- /dev/null +++ b/src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptions.cs @@ -0,0 +1,114 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-02-08 07:41 +// +// Last Modified By : RzR +// Last Modified On : 2023-02-10 04:16 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#if NETSTANDARD2_0_OR_GREATER || NET || NETCOREAPP3_1_OR_GREATER + +#region U S A G E S + +using DomainCommonExtensions.DataTypeExtensions; +using Microsoft.Extensions.Options; +using MLogHelperDotNet.Extensions; +using MLogHelperDotNet.Models; +using System; +using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; + +// ReSharper disable CollectionNeverUpdated.Local + +#endregion + +namespace MLogHelperDotNet.Configurations.Options +{ + /// + /// MLog POST configuration options + /// + public class MLogPostConfigureOptions : IPostConfigureOptions + { + /// + public void PostConfigure(string name, RemoteMLogOptions mLogOptions) + { + if (mLogOptions.ServiceClientAddress.IsNullOrWhiteSpace()) + { + throw new ArgumentException($"Please provide a {nameof(mLogOptions.ServiceClientAddress)}"); + } + + if (mLogOptions.ServiceCertificatePath.IsNullOrWhiteSpace()) + { + throw new ArgumentException($"Please provide a {nameof(mLogOptions.ServiceCertificatePath)}"); + } + + if (mLogOptions.ServiceCertificatePassword.IsNullOrWhiteSpace()) + { + throw new ArgumentException($"Please provide a {nameof(mLogOptions.ServiceCertificatePassword)}"); + } + + if (mLogOptions.ServiceEventRegisterPath.IsNullOrEmpty()) + { + mLogOptions.ServiceEventRegisterPath = "register"; + } + + if (mLogOptions.ServiceEventGetPath.IsNullOrEmpty()) + { + mLogOptions.ServiceEventGetPath = "query"; + } + + var certificate = new X509Certificate2Collection(CertificateLoader.Private( + mLogOptions.ServiceCertificatePath, + mLogOptions.ServiceCertificatePassword)); + + if (certificate.Count.IsZero()) + { + throw new ApplicationException("Invalid service certificate path or password"); + } + + if (certificate.Count.IsGreaterThanZero()) + { + mLogOptions.ServiceCertificate = certificate[0]; + } + + mLogOptions.BasicHttpsBinding = new BasicHttpsBinding + { + Security = + { + Transport = new HttpTransportSecurity + { + ClientCredentialType = HttpClientCredentialType.Certificate + }, + Mode = BasicHttpsSecurityMode.Transport + }, + MaxReceivedMessageSize = 2147483647, + CloseTimeout = TimeSpan.FromMinutes(20) + }; + + mLogOptions.BasicHttpBinding = new BasicHttpBinding + { + Security = + { + Transport = new HttpTransportSecurity + { + ClientCredentialType = HttpClientCredentialType.Certificate + }, + Mode = BasicHttpSecurityMode.Transport + }, + MaxReceivedMessageSize = 2147483647, + CloseTimeout = TimeSpan.FromMinutes(20) + }; + + mLogOptions.EndpointAddress = new EndpointAddress(mLogOptions.ServiceClientAddress); + } + } +} +#endif \ No newline at end of file diff --git a/src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptionsNetFramework.cs b/src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptionsNetFramework.cs new file mode 100644 index 0000000..b9c7e27 --- /dev/null +++ b/src/MLogHelperDotNet/Configurations/Options/MLogPostConfigureOptionsNetFramework.cs @@ -0,0 +1,134 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-02-08 07:41 +// +// Last Modified By : RzR +// Last Modified On : 2023-02-10 04:19 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#if NET45 + +#region U S A G E S + +using System; +using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; +using DomainCommonExtensions.DataTypeExtensions; +using MLogHelperDotNet.Configurations; +using MLogHelperDotNet.Extensions; +using MLogHelperDotNet.Helpers; +using MLogHelperDotNet.Models; +// ReSharper disable CollectionNeverUpdated.Local + +#endregion + +namespace MNotifyHelperDotNet.Configurations.Options +{ + /// + /// Post sign configuration + /// + /// + internal static class MLogPostConfigureOptionsNetFramework + { + internal static RemoteMLogOptions InitOptions() + { + var serviceConfigurationOptions = MLogConfigurationSectionNetFramework.GetRemoteMLogOptions(); + + var remoteServiceClientAddress = serviceConfigurationOptions.ServiceClientAddress; + if (remoteServiceClientAddress.IsNullOrEmpty()) + { + throw new ArgumentException($"Please provide a {nameof(serviceConfigurationOptions.ServiceClientAddress)}"); + } + + var serviceCertificatePath = serviceConfigurationOptions.ServiceCertificatePath; + if (serviceCertificatePath.IsNullOrEmpty()) + { + throw new ArgumentException($"Please provide a {nameof(serviceConfigurationOptions.ServiceCertificatePath)}"); + } + + var serviceCertificatePassword = serviceConfigurationOptions.ServiceCertificatePassword; + if (serviceCertificatePassword.IsNullOrEmpty()) + { + throw new ArgumentException($"Please provide a {nameof(serviceConfigurationOptions.ServiceCertificatePassword)}"); + } + + var serviceEventRegisterPath = serviceConfigurationOptions.ServiceEventRegisterPath; + if (serviceEventRegisterPath.IsNullOrEmpty()) + { + serviceConfigurationOptions.ServiceEventRegisterPath = "register"; + } + + var serviceEventGetPath = serviceConfigurationOptions.ServiceEventGetPath; + if (serviceEventGetPath.IsNullOrEmpty()) + { + serviceConfigurationOptions.ServiceEventGetPath = "query"; + } + + var mLogOptions = new RemoteMLogOptions + { + ServiceEventRegisterPath = serviceConfigurationOptions.ServiceEventRegisterPath, + ServiceEventGetPath = serviceConfigurationOptions.ServiceEventGetPath, + ServiceClientAddress = remoteServiceClientAddress, + ServiceCertificatePath = serviceCertificatePath, + ServiceCertificatePassword = serviceCertificatePassword, + BasicHttpBinding = new BasicHttpBinding + { + Security = + { + Transport = new HttpTransportSecurity + { + ClientCredentialType = HttpClientCredentialType.Certificate + }, + Mode = BasicHttpSecurityMode.Transport + }, + MaxReceivedMessageSize = 2147483647, + CloseTimeout = TimeSpan.FromMinutes(5) + }, + BasicHttpsBinding = new BasicHttpsBinding + { + Security = + { + Transport = new HttpTransportSecurity + { + ClientCredentialType = HttpClientCredentialType.Certificate + }, + Mode = BasicHttpsSecurityMode.Transport + }, + MaxReceivedMessageSize = 2147483647, + CloseTimeout = TimeSpan.FromMinutes(5) + } + }; + + if (!serviceConfigurationOptions.ServiceTimeoutInMinute.IsNullOrWhiteSpace()) + mLogOptions.ServiceTimeoutInMinute = serviceConfigurationOptions.ServiceTimeoutInMinute; + + var certificate = new X509Certificate2Collection(CertificateLoader.Private( + mLogOptions.ServiceCertificatePath, + mLogOptions.ServiceCertificatePassword)); + + if (certificate.Count.IsZero()) + { + throw new ApplicationException("Invalid service certificate path or password"); + } + + if (certificate.Count.IsGreaterThanZero()) + { + mLogOptions.ServiceCertificate = certificate[0]; + } + + mLogOptions.EndpointAddress = new EndpointAddress(mLogOptions.ServiceClientAddress); + + return mLogOptions; + } + } +} + +#endif \ No newline at end of file diff --git a/src/MLogHelperDotNet/DependencyInjection.cs b/src/MLogHelperDotNet/DependencyInjection.cs new file mode 100644 index 0000000..9b0c9bd --- /dev/null +++ b/src/MLogHelperDotNet/DependencyInjection.cs @@ -0,0 +1,96 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-01 09:50 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-03 21:31 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#if NETSTANDARD2_0_OR_GREATER || NET || NETCOREAPP3_1_OR_GREATER + +#region U S A G E S + +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using MLogHelperDotNet.Abstractions; +using MLogHelperDotNet.Configurations.Options; +using MLogHelperDotNet.Enums; +using MLogHelperDotNet.Extensions; +using MLogHelperDotNet.Models; +using MLogHelperDotNet.Services; +using System; + +#endregion + +namespace MLogHelperDotNet +{ + /// + /// MLog service DI + /// + /// + public static class DependencyInjection + { + /// + /// Add MLog service + /// + /// Service collection + /// App configuration + /// + /// + public static IServiceCollection AddMLogService(this IServiceCollection services, IConfiguration configuration) + { + services.AddHttpClient(); + services.AddOptions(); + var mLogOptions = configuration.GetSection(nameof(RemoteMLogOptions)).Get(); + + services.Configure(options => + { + options.ServiceClientAddress = mLogOptions.ServiceClientAddress; + options.ServiceEventRegisterPath = mLogOptions.ServiceEventRegisterPath; + options.ServiceEventGetPath = mLogOptions.ServiceEventGetPath; + options.ServiceCertificatePath = mLogOptions.ServiceCertificatePath; + options.ServiceCertificatePassword = mLogOptions.ServiceCertificatePassword; + if (!mLogOptions.ServiceTimeoutInMinute.IsNullOrWhiteSpace()) + options.ServiceTimeoutInMinute = mLogOptions.ServiceTimeoutInMinute; + }); + services.PostConfigure(options => + { + options.ServiceClientAddress = mLogOptions.ServiceClientAddress; + options.ServiceEventRegisterPath = mLogOptions.ServiceEventRegisterPath; + options.ServiceEventGetPath = mLogOptions.ServiceEventGetPath; + options.ServiceCertificatePath = mLogOptions.ServiceCertificatePath; + options.ServiceCertificatePassword = mLogOptions.ServiceCertificatePassword; + if (!mLogOptions.ServiceTimeoutInMinute.IsNullOrWhiteSpace()) + options.ServiceTimeoutInMinute = mLogOptions.ServiceTimeoutInMinute; + }); + services.AddSingleton(mLogOptions); + + services.AddSingleton, MLogPostConfigureOptions>(); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddSingleton>(sp => endpointType => + { + return endpointType switch + { + EndpointType.REST => sp.GetRequiredService(), + EndpointType.SOAP => throw new NotImplementedException($"SOAP implementation currently is not available!"), + _ => throw new NotImplementedException($"No implementation for endpoint type: {endpointType}") + }; + }); + + return services; + } + } +} +#endif \ No newline at end of file diff --git a/src/MLogHelperDotNet/Enums/EndpointType.cs b/src/MLogHelperDotNet/Enums/EndpointType.cs new file mode 100644 index 0000000..2c1e4f0 --- /dev/null +++ b/src/MLogHelperDotNet/Enums/EndpointType.cs @@ -0,0 +1,36 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-07 00:35 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-07 00:35 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +// ReSharper disable InconsistentNaming +namespace MLogHelperDotNet.Enums +{ + ///------------------------------------------------------------------------------------------------- + /// End point type. + /// + ///================================================================================================= + public enum EndpointType + { + /// + /// SOAP + /// + SOAP, + + /// + /// REST + /// + REST + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Enums/MLogLevelType.cs b/src/MLogHelperDotNet/Enums/MLogLevelType.cs new file mode 100644 index 0000000..e3c0d7f --- /dev/null +++ b/src/MLogHelperDotNet/Enums/MLogLevelType.cs @@ -0,0 +1,46 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-18 22:12 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-18 22:13 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +namespace MLogHelperDotNet.Enums +{ + ///------------------------------------------------------------------------------------------------- + /// Values that represent log level types. + /// + ///================================================================================================= + public enum MLogLevelType + { + /// An enum constant representing the unknown option. + Unknown = 0, + + /// An enum constant representing the fatal option. + Fatal = 1, + + /// An enum constant representing the error option. + Error = 2, + + /// An enum constant representing the warning option. + Warning = 3, + + /// An enum constant representing the information option. + Information = 4, + + /// An enum constant representing the debug option. + Debug = 5, + + /// An enum constant representing the trace option. + Trace = 6 + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Extensions/HttpClientExtension.cs b/src/MLogHelperDotNet/Extensions/HttpClientExtension.cs new file mode 100644 index 0000000..9393702 --- /dev/null +++ b/src/MLogHelperDotNet/Extensions/HttpClientExtension.cs @@ -0,0 +1,169 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-04 22:47 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-04 23:51 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using AggregatedGenericResultMessage; +using AggregatedGenericResultMessage.Abstractions; +using AggregatedGenericResultMessage.Extensions.Result; +using DomainCommonExtensions.DataTypeExtensions; +using System; +using System.Net.Http; +using System.Threading.Tasks; + +#endregion + +namespace MLogHelperDotNet.Extensions +{ + /// ------------------------------------------------------------------------------------------------- + /// A HTTP client extension. + /// + /// ================================================================================================= + internal static class HttpClientExtension + { + /// ------------------------------------------------------------------------------------------------- + /// A HttpClient extension method that HTTP post. + /// RzR, 04-Dec-23. + /// The client to act on. + /// URL of the register. + /// The content. + /// An IResult<string> + /// ================================================================================================= + internal static IResult HttpPost(this HttpClient client, string registerUrl, string content) + { + client.ThrowArgIfNull(nameof(client)); + registerUrl.ThrowArgIfNull(nameof(registerUrl)); + content.ThrowArgIfNull(nameof(content)); + + try + { + string resultMessage; + using (var response = client.PostAsync(registerUrl, new StringContent(content)).GetAwaiter().GetResult()) + { + response.EnsureSuccessStatusCode(); + using var result = response.Content; + resultMessage = result.ReadAsStringAsync().GetAwaiter().GetResult(); + } + + return Result.Success(resultMessage); + } + catch (Exception e) + { + return Result.Failure(e.Message) + .WithError(e); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// A HttpClient extension method that HTTP post asynchronous. + /// RzR, 04-Dec-23. + /// The client to act on. + /// URL of the register. + /// The content. + /// The HTTP post. + /// ================================================================================================= + internal static async Task> HttpPostAsync(this HttpClient client, string registerUrl, string content) + { + client.ThrowArgIfNull(nameof(client)); + registerUrl.ThrowArgIfNull(nameof(registerUrl)); + content.ThrowArgIfNull(nameof(content)); + + try + { + string resultMessage; + using (var response = await client.PostAsync(registerUrl, new StringContent(content))) + { + response.EnsureSuccessStatusCode(); + using var result = response.Content; + resultMessage = await result.ReadAsStringAsync(); + } + + return Result.Success(resultMessage); + } + catch (Exception e) + { + return Result.Failure(e.Message) + .WithError(e); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// A HttpClient extension method that HTTP get. + /// RzR, 04-Dec-23. + /// The client to act on. + /// URL of the query. + /// The query. + /// An IResult<string> + /// ================================================================================================= + internal static IResult HttpGet(this HttpClient client, string queryUrl, string query) + { + client.ThrowArgIfNull(nameof(client)); + queryUrl.ThrowArgIfNull(nameof(queryUrl)); + query.ThrowArgIfNull(nameof(query)); + + try + { + string resultMessage; + using (var response = client.GetAsync(queryUrl + query).GetAwaiter().GetResult()) + { + response.EnsureSuccessStatusCode(); + using var content = response.Content; + resultMessage = content.ReadAsStringAsync().GetAwaiter().GetResult(); + } + + return Result.Success(resultMessage); + } + catch (Exception e) + { + return Result.Failure(e.Message) + .WithError(e); + } + } + + /// ------------------------------------------------------------------------------------------------- + /// A HttpClient extension method that HTTP get asynchronous. + /// RzR, 04-Dec-23. + /// The client to act on. + /// URL of the query. + /// The query. + /// The HTTP get. + /// ================================================================================================= + internal static async Task> HttpGetAsync(this HttpClient client, string queryUrl, string query) + { + client.ThrowArgIfNull(nameof(client)); + queryUrl.ThrowArgIfNull(nameof(queryUrl)); + query.ThrowArgIfNull(nameof(query)); + + try + { + string resultMessage; + using (var response = await client.GetAsync(queryUrl + query)) + { + response.EnsureSuccessStatusCode(); + using var content = response.Content; + resultMessage = await content.ReadAsStringAsync(); + } + + return Result.Success(resultMessage); + } + catch (Exception e) + { + return Result.Failure(e.Message) + .WithError(e); + } + } + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Extensions/StringExtensions.cs b/src/MLogHelperDotNet/Extensions/StringExtensions.cs new file mode 100644 index 0000000..8779e0a --- /dev/null +++ b/src/MLogHelperDotNet/Extensions/StringExtensions.cs @@ -0,0 +1,42 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-02 00:59 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-02 01:10 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using DomainCommonExtensions.DataTypeExtensions; + +#endregion + +namespace MLogHelperDotNet.Extensions +{ + /// ------------------------------------------------------------------------------------------------- + /// A string extensions. + /// + /// ================================================================================================= + internal static class StringExtensions + { + /// ------------------------------------------------------------------------------------------------- + /// + /// A string extension method that query if 'source' is null or white space. + /// + /// RzR, 02-Dec-23. + /// The source to act on. + /// True if null or white space, false if not. + /// ================================================================================================= + internal static bool IsNullOrWhiteSpace(this string source) + => source.IsNullOrEmpty() || string.IsNullOrWhiteSpace(source); + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Extensions/TExtensions.cs b/src/MLogHelperDotNet/Extensions/TExtensions.cs new file mode 100644 index 0000000..f3f354e --- /dev/null +++ b/src/MLogHelperDotNet/Extensions/TExtensions.cs @@ -0,0 +1,50 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-01 09:50 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-02 01:11 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using System; +using System.Linq; + +#endregion + +// ReSharper disable InconsistentNaming + +namespace MLogHelperDotNet.Extensions +{ + /// ------------------------------------------------------------------------------------------------- + /// T extensions. + /// RzR, 02-Dec-23. + /// ================================================================================================= + public static class TExtensions + { + /// ------------------------------------------------------------------------------------------------- + /// Get sub array. + /// RzR, 02-Dec-23. + /// Source type. + /// Source array. + /// Offset. + /// Take. + /// A T[]. + /// ================================================================================================= + public static T[] SubArray(this T[] array, int offset, int length) => array.Skip(offset) + .Take(length) + .ToArray(); + + internal static bool IsTypeOfDateTime(this Type sourceType) + => sourceType == typeof(DateTime) || sourceType == typeof(DateTime?); + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Helpers/ConfSection/MLogConfigurationSection.cs b/src/MLogHelperDotNet/Helpers/ConfSection/MLogConfigurationSection.cs new file mode 100644 index 0000000..980e76c --- /dev/null +++ b/src/MLogHelperDotNet/Helpers/ConfSection/MLogConfigurationSection.cs @@ -0,0 +1,96 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-02-13 22:11 +// +// Last Modified By : RzR +// Last Modified On : 2023-02-13 22:58 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#if NET45 + +#region U S A G E S + +using System.Configuration; + +#endregion + +// ReSharper disable ClassNeverInstantiated.Global + +namespace MLogHelperDotNet.Helpers.ConfSection +{ + ///------------------------------------------------------------------------------------------------- + /// MLog service configuration sections. + /// RzR, 02-Dec-23. + /// + ///================================================================================================= + internal class MLogConfigurationSection : ConfigurationSection + { + /// (Immutable) name of the collection. + private const string _collectionName = "RemoteMLogOptions"; + + ///------------------------------------------------------------------------------------------------- + /// MLog service options. + /// Options that control the service. + ///================================================================================================= + [ConfigurationProperty(_collectionName)] + [ConfigurationCollection(typeof(MLogRemoteOptionElementCollection), AddItemName = "option")] + internal MLogRemoteOptionElementCollection ServiceOptions + => (MLogRemoteOptionElementCollection)base[_collectionName]; + } + + ///------------------------------------------------------------------------------------------------- + /// MLog remote service configuration element collection. + /// RzR, 02-Dec-23. + /// + ///================================================================================================= + internal class MLogRemoteOptionElementCollection : ConfigurationElementCollection + { + /// + protected override ConfigurationElement CreateNewElement() + => new MLogOptionConfigurationElement(); + + /// + protected override object GetElementKey(ConfigurationElement element) + => ((MLogOptionConfigurationElement)element).Key; + } + + ///------------------------------------------------------------------------------------------------- + /// MLog option configuration element. + /// RzR, 02-Dec-23. + /// + ///================================================================================================= + internal class MLogOptionConfigurationElement : ConfigurationElement + { + ///------------------------------------------------------------------------------------------------- + /// Option key. + /// The key. + ///================================================================================================= + [ConfigurationProperty("key", IsRequired = true, IsKey = true)] + internal string Key + { + get => (string)this["key"]; + set => this["key"] = value; + } + + ///------------------------------------------------------------------------------------------------- + /// Option value. + /// The value. + ///================================================================================================= + [ConfigurationProperty("value", IsRequired = true)] + internal string Value + { + get => (string)this["value"]; + set => this["value"] = value; + } + } +} + +#endif \ No newline at end of file diff --git a/src/MLogHelperDotNet/Helpers/JsonHelper.cs b/src/MLogHelperDotNet/Helpers/JsonHelper.cs new file mode 100644 index 0000000..a91dc71 --- /dev/null +++ b/src/MLogHelperDotNet/Helpers/JsonHelper.cs @@ -0,0 +1,105 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-18 23:23 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-18 23:30 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using DomainCommonExtensions.CommonExtensions; +using System.Text; + +#endregion + +namespace MLogHelperDotNet.Helpers +{ + /// ------------------------------------------------------------------------------------------------- + /// A JSON helper. + /// + /// ================================================================================================= + internal static class JsonHelper + { + ///------------------------------------------------------------------------------------------------- + /// Escape object. + /// + /// The object. + /// A string. + ///================================================================================================= + internal static string EscapeObject(object obj) + { + var result = new StringBuilder(); + AppendEscapedJsonObject(result, obj); + + return result.ToString(); + } + + ///------------------------------------------------------------------------------------------------- + /// Appends an escaped JSON object. + /// + /// The result. + /// The object. + ///================================================================================================= + internal static void AppendEscapedJsonObject(StringBuilder result, object obj) + { + if (obj.IsNull()) + { + result.Append("null"); + } + else + { + var objectValue = obj.ToString(); + result.Append('"'); + var length = objectValue.Length; + for (var index = 0; index < length; ++index) + { + var charValue = objectValue[index]; + switch (charValue) + { + case '\b': + result.Append("\\b"); + break; + case '\t': + result.Append("\\t"); + break; + case '\n': + result.Append("\\n"); + break; + case '\f': + result.Append("\\f"); + break; + case '\r': + result.Append("\\r"); + break; + case '"': + case '/': + case '\\': + result.Append('\\'); + result.Append(charValue); + break; + default: + if (charValue < ' ') + { + result.Append("\\u" + ((int)charValue).ToString("x4")); + break; + } + + result.Append(charValue); + break; + } + } + + result.Append('"'); + } + } + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Helpers/MLogConfigurationSectionNetFramework.cs b/src/MLogHelperDotNet/Helpers/MLogConfigurationSectionNetFramework.cs new file mode 100644 index 0000000..780d498 --- /dev/null +++ b/src/MLogHelperDotNet/Helpers/MLogConfigurationSectionNetFramework.cs @@ -0,0 +1,102 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-02-13 22:40 +// +// Last Modified By : RzR +// Last Modified On : 2023-02-13 22:52 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#if NET45 + +#region U S A G E S + +using System; +using System.Configuration; +using MLogHelperDotNet.Models; +using MLogHelperDotNet.Helpers.ConfSection; +using DomainCommonExtensions.DataTypeExtensions; +using DomainCommonExtensions.CommonExtensions; + +#endregion + +namespace MLogHelperDotNet.Helpers +{ + ///------------------------------------------------------------------------------------------------- + /// MLog configuration section NET Framework. + /// RzR, 02-Dec-23. + ///================================================================================================= + internal static class MLogConfigurationSectionNetFramework + { + ///------------------------------------------------------------------------------------------------- + /// Get MLog configuration options. + /// RzR, 02-Dec-23. + /// + /// Thrown when one or more required arguments are null. + /// + /// + /// Thrown when one or more arguments are outside the required range. + /// + /// The remote m log options. + ///================================================================================================= + internal static RemoteMLogOptions GetRemoteMLogOptions() + { + var configuration = new RemoteMLogOptions(); + var configurationOptions = ConfigurationManager.GetSection("MLogConfigurationSection") as MLogConfigurationSection; + if (configurationOptions.IsNull() || configurationOptions?.ServiceOptions.Count == 0) + { + throw new ArgumentNullException($@"{nameof(configurationOptions)} is null or empty"); + } + + // ReSharper disable once PossibleNullReferenceException + foreach (MLogOptionConfigurationElement element in configurationOptions?.ServiceOptions) + { + if (element.Key.IsNullOrEmpty()) + { + throw new ArgumentNullException($@"{nameof(element.Key)} is null"); + } + + if (element.Value.IsNullOrEmpty()) + { + throw new ArgumentNullException($@"{nameof(element.Value)} is null"); + } + + switch (element.Key) + { + case nameof(configuration.ServiceClientAddress): + configuration.ServiceClientAddress = element.Value; + break; + case nameof(configuration.ServiceEventRegisterPath): + configuration.ServiceEventRegisterPath = element.Value; + break; + case nameof(configuration.ServiceEventGetPath): + configuration.ServiceEventGetPath = element.Value; + break; + case nameof(configuration.ServiceCertificatePath): + configuration.ServiceCertificatePath = element.Value; + break; + case nameof(configuration.ServiceCertificatePassword): + configuration.ServiceCertificatePassword = element.Value; + break; + case nameof(configuration.ServiceTimeoutInMinute): + if (!string.IsNullOrEmpty(element.Value)) + configuration.ServiceTimeoutInMinute = element.Value; + break; + default: + throw new ArgumentOutOfRangeException(@$"{nameof(element.Key)} is out of range"); + } + } + + return configuration; + } + } +} + +#endif \ No newline at end of file diff --git a/src/MLogHelperDotNet/MLogHelperDotNet.DotSettings b/src/MLogHelperDotNet/MLogHelperDotNet.DotSettings new file mode 100644 index 0000000..e7a6e3d --- /dev/null +++ b/src/MLogHelperDotNet/MLogHelperDotNet.DotSettings @@ -0,0 +1,3 @@ + + CSharp90 + \ No newline at end of file diff --git a/src/MLogHelperDotNet/MLogHelperDotNet.csproj b/src/MLogHelperDotNet/MLogHelperDotNet.csproj new file mode 100644 index 0000000..c749c18 --- /dev/null +++ b/src/MLogHelperDotNet/MLogHelperDotNet.csproj @@ -0,0 +1,99 @@ + + + + net45;netstandard2.0;netstandard2.1;net5.0;netcoreapp3.1 + false + RzR + RzR + RzR;eGOV + LICENSE + PackageIcon.png + $(NoWarn);CS8032 + false + false + MLog;e-GOV;MD;RM;Helper;Wrapper;GOV.MD;AGE;EGA;DotNet;Framework;Log;Service + https://github.com/I-RzR-I/eGovMD-MLogHelperDotNet + https://github.com/I-RzR-I/eGovMD-MLogHelperDotNet + true + A wrapper for governmental logging service for a quick way to implement it with a few configuration settings. Service is provided by an e-governance agency (https://egov.md/), named `MLog`, available in the Republic of Moldova. + A wrapper for governmental logging service for a quick way to implement it with a few configuration settings. Service is provided by an e-governance agency (https://egov.md/), named `MLog`, available in the Republic of Moldova. + RzR.Shared.eGovMD-MLogHelperDotNet (e-GOV MD MLog helper) + RzR.Shared.eGovMD-MLogHelperDotNet (e-GOV MD MLog helper) + 9.0 + GIT + en-US + + Full + + + + + + + + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + + + + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + + + + + True + + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/MLogHelperDotNet/Models/MLogEventDto.cs b/src/MLogHelperDotNet/Models/MLogEventDto.cs new file mode 100644 index 0000000..98a0871 --- /dev/null +++ b/src/MLogHelperDotNet/Models/MLogEventDto.cs @@ -0,0 +1,349 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-18 22:22 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-18 23:35 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using DomainCommonExtensions.CommonExtensions; +using MLogHelperDotNet.Extensions; +using MLogHelperDotNet.Helpers; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Text; +// ReSharper disable ClassNeverInstantiated.Global +// ReSharper disable RedundantDictionaryContainsKeyBeforeAdding +// ReSharper disable InconsistentNaming + +#endregion + +namespace MLogHelperDotNet.Models +{ + /// ------------------------------------------------------------------------------------------------- + /// A log event data transfer object. + /// + /// ================================================================================================= + [Serializable] + [DataContract] + public class MLogEventDto + { + /// (Immutable) the properties. + private readonly Dictionary properties = new Dictionary(); + + /// ------------------------------------------------------------------------------------------------- + /// Constructor. + /// + /// ================================================================================================= + public MLogEventDto() { } + + /// ------------------------------------------------------------------------------------------------- + /// Constructor. + /// + /// The type of the event. + /// ================================================================================================= + public MLogEventDto(string eventType) + { + EventTime = DateTime.Now; + EventType = eventType; + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the event time. + /// The event time. + /// ================================================================================================= + [DataMember] + public DateTime EventTime + { + get => GetProperty("event_time") as DateTime? ?? DateTime.Now; + set => SetProperty("event_time", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the type of the event. + /// The type of the event. + /// ================================================================================================= + [DataMember] + public string EventType + { + get => GetProperty("event_type")?.ToString(); + set => SetProperty("event_type", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the identifier of the event. + /// The identifier of the event. + /// ================================================================================================= + [DataMember] + public string EventID + { + get => GetProperty("event_id")?.ToString(); + set => SetProperty("event_id", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the event correlation. + /// The event correlation. + /// ================================================================================================= + [DataMember] + public string EventCorrelation + { + get => GetProperty("event_correlation")?.ToString(); + set => SetProperty("event_correlation", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the event level. + /// The event level. + /// ================================================================================================= + [DataMember] + public string EventLevel + { + get => GetProperty("event_level")?.ToString(); + set => SetProperty("event_level", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the event source. + /// The event source. + /// ================================================================================================= + [DataMember] + public string EventSource + { + get => GetProperty("event_source")?.ToString(); + set => SetProperty("event_source", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets a message describing the event. + /// A message describing the event. + /// ================================================================================================= + [DataMember] + public string EventMessage + { + get => GetProperty("event_message")?.ToString(); + set => SetProperty("event_message", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the event details. + /// The event details. + /// ================================================================================================= + [DataMember] + public object EventDetails + { + get => GetProperty("event_details"); + set => SetProperty("event_details", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the legal entity. + /// The legal entity. + /// ================================================================================================= + [DataMember] + public string LegalEntity + { + get => GetProperty("legal_entity")?.ToString(); + set => SetProperty("legal_entity", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the legal basis. + /// The legal basis. + /// ================================================================================================= + [DataMember] + public object LegalBasis + { + get => GetProperty("legal_basis"); + set => SetProperty("legal_basis", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the legal reason. + /// The legal reason. + /// ================================================================================================= + [DataMember] + public object LegalReason + { + get => GetProperty("legal_reason"); + set => SetProperty("legal_reason", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the user. + /// The user. + /// ================================================================================================= + [DataMember] + public string User + { + get => GetProperty("user")?.ToString(); + set => SetProperty("user", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the user session. + /// The user session. + /// ================================================================================================= + [DataMember] + public string UserSession + { + get => GetProperty("user_session")?.ToString(); + set => SetProperty("user_session", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the user address. + /// The user address. + /// ================================================================================================= + [DataMember] + public string UserAddress + { + get => GetProperty("user_address")?.ToString(); + set => SetProperty("user_address", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the subject. + /// The subject. + /// ================================================================================================= + [DataMember] + public object Subject + { + get => GetProperty("subject"); + set => SetProperty("subject", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the type of the subject. + /// The type of the subject. + /// ================================================================================================= + [DataMember] + public object SubjectType + { + get => GetProperty("subject_type"); + set => SetProperty("subject_type", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the name of the subject. + /// The name of the subject. + /// ================================================================================================= + [DataMember] + public string SubjectName + { + get => GetProperty("subject_name")?.ToString(); + set => SetProperty("subject_name", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the object. + /// The object. + /// ================================================================================================= + [DataMember] + public object Object + { + get => GetProperty("object"); + set => SetProperty("object", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the type of the object. + /// The type of the object. + /// ================================================================================================= + [DataMember] + public string ObjectType + { + get => GetProperty("object_type")?.ToString(); + set => SetProperty("object_type", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets or sets the name of the object. + /// The name of the object. + /// ================================================================================================= + [DataMember] + public string ObjectName + { + get => GetProperty("object_name")?.ToString(); + set => SetProperty("object_name", value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Sets a property. + /// + /// The key. + /// The value. + /// ================================================================================================= + private void SetProperty(string key, object value) + { + if (value.IsNull() || (value is string str && str.IsNullOrWhiteSpace()) || value is ICollection { Count: 0 }) + properties.Remove(key); + else if (properties.ContainsKey(key)) + properties[key] = value; + else + properties.Add(key, value); + } + + /// ------------------------------------------------------------------------------------------------- + /// Gets a property. + /// + /// The key. + /// The property. + /// ================================================================================================= + private object GetProperty(string key) + { + properties.TryGetValue(key, out var property); + + return property; + } + + /// + public override string ToString() + { + var result = new StringBuilder("{", properties.Count * 32); + foreach (var property in properties) + { + if (property.Value.IsNotNull()) + { + if (result.Length > 1) + result.Append(','); + JsonHelper.AppendEscapedJsonObject(result, property.Key); + result.Append(":"); + + if (property.Value.GetType().IsTypeOfDateTime()) + { + var dateTime = (DateTime?)property.Value; + + result.Append('"'); + result.Append(dateTime?.ToString("O")); + result.Append('"'); + } + else + { + var serializer = new System.Runtime.Serialization.Json.DataContractJsonSerializer(property.Value.GetType()); + var ms = new MemoryStream(); + serializer.WriteObject(ms, property.Value); + var json = Encoding.Default.GetString(ms.ToArray()); + + result.Append(json); + } + } + } + + return result.Append('}').ToString(); + } + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Models/RemoteMLogOptions.cs b/src/MLogHelperDotNet/Models/RemoteMLogOptions.cs new file mode 100644 index 0000000..ca24f90 --- /dev/null +++ b/src/MLogHelperDotNet/Models/RemoteMLogOptions.cs @@ -0,0 +1,94 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-02-08 07:41 +// +// Last Modified By : RzR +// Last Modified On : 2023-02-10 08:23 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using System.Security.Cryptography.X509Certificates; +using System.ServiceModel; + +// ReSharper disable ClassNeverInstantiated.Global + +#endregion + +namespace MLogHelperDotNet.Models +{ + ///------------------------------------------------------------------------------------------------- + /// Remote MLog option. + /// + ///================================================================================================= + public class RemoteMLogOptions + { + ///------------------------------------------------------------------------------------------------- + /// Gets or sets link/address to remote service. + /// The service client address. + ///================================================================================================= + public string ServiceClientAddress { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Gets or sets path to event registration. + /// The full pathname of the service event register file. + ///================================================================================================= + public string ServiceEventRegisterPath { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Gets or sets path to event get/query. + /// The full pathname of the service event get file. + ///================================================================================================= + public string ServiceEventGetPath { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Path of service certificate. + /// The full pathname of the service certificate file. + ///================================================================================================= + public string ServiceCertificatePath { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Password for service certificate. + /// The service certificate password. + ///================================================================================================= + public string ServiceCertificatePassword { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Timeout for service call. + /// The service certificate password. + ///================================================================================================= + public string ServiceTimeoutInMinute { get; set; } = "5"; + + ///------------------------------------------------------------------------------------------------- + /// Service certificate. + /// The service certificate. + ///================================================================================================= + public X509Certificate2 ServiceCertificate { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Gets or sets BasicHttpsBinding. + /// The basic HTTPS binding. + ///================================================================================================= + public BasicHttpsBinding BasicHttpsBinding { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Gets or sets BasicHttpBinding. + /// The basic HTTP binding. + ///================================================================================================= + public BasicHttpBinding BasicHttpBinding { get; set; } + + ///------------------------------------------------------------------------------------------------- + /// Gets or sets EndpointAddress. + /// The endpoint address. + ///================================================================================================= + public EndpointAddress EndpointAddress { get; set; } + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Services/MLogRestClientService.cs b/src/MLogHelperDotNet/Services/MLogRestClientService.cs new file mode 100644 index 0000000..203998e --- /dev/null +++ b/src/MLogHelperDotNet/Services/MLogRestClientService.cs @@ -0,0 +1,198 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-08 00:05 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-19 21:59 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + + +#region U S A G E S + +using AggregatedGenericResultMessage; +using AggregatedGenericResultMessage.Abstractions; +using DomainCommonExtensions.ArraysExtensions; +using MLogHelperDotNet.Abstractions; +using MLogHelperDotNet.Extensions; +using MLogHelperDotNet.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +#if NETSTANDARD2_0_OR_GREATER || NET || NETCOREAPP3_1_OR_GREATER +using Microsoft.Extensions.Options; +using System.Net.Http; +using AggregatedGenericResultMessage.Models; +#endif + +#if NET45 +using MNotifyHelperDotNet.Configurations.Options; +using System.Net.Http; +using System.Security.Cryptography.X509Certificates; +using AggregatedGenericResultMessage.Models; +#endif + +#endregion + +namespace MLogHelperDotNet.Services +{ + /// ------------------------------------------------------------------------------------------------- + /// A log client. MLog client + /// + /// ================================================================================================= + public class MLogRestClientService : IMLogEndpointClientService, IDisposable + { + /// + /// Remote logging options + /// + private readonly RemoteMLogOptions _remoteMLogOptions; + + private HttpClient _client; + private Uri _registerUrl; + +#if NETSTANDARD2_0_OR_GREATER || NET || NETCOREAPP3_1_OR_GREATER + + /// ------------------------------------------------------------------------------------------------- + /// Constructor. + /// + /// Options for controlling the operation. + /// ================================================================================================= + public MLogRestClientService(IOptions options) + { + _remoteMLogOptions = options.Value; + + InitRequired(); + } + +#endif + + /// ------------------------------------------------------------------------------------------------- + /// Constructor. + /// + /// [in,out] Options for controlling the operation. + /// ================================================================================================= + public MLogRestClientService(ref RemoteMLogOptions options) + { + _remoteMLogOptions = options; + + InitRequired(); + } + + /// ------------------------------------------------------------------------------------------------- + /// Default constructor. + /// RzR, 12-Dec-23. + /// ================================================================================================= + public MLogRestClientService() + { +#if NET45 + _remoteMLogOptions = MLogPostConfigureOptionsNetFramework.InitOptions(); + + InitRequired(); +#endif + } + + /// + public void Dispose() => _client?.Dispose(); + + /// + public IResult RegisterEvent(string jsonEvent) + { + if (jsonEvent.IsNullOrWhiteSpace()) + { + return Result + .Failure($"Cannot register empty event, ${nameof(jsonEvent)}"); + } + + var registerResult = _client.HttpPost(_registerUrl.ToString(), jsonEvent); + + return registerResult; + } + + /// + public IResult RegisterEventBatch(IReadOnlyCollection jsonEvents) + { + if (jsonEvents.IsNullOrEmptyEnumerable()) + { + return Result + .Failure(new MessageDataModel("No events provided for registration", + $"No events provided for registration, ${nameof(jsonEvents)}")); + } + + return RegisterEvent(string.Join(Environment.NewLine, jsonEvents)); + } + + /// + public async Task> RegisterEventAsync(string jsonEvent) + { + if (jsonEvent.IsNullOrWhiteSpace()) + { + return Result + .Failure(new MessageDataModel("Cannot register empty event", + $"Cannot register empty event, ${nameof(jsonEvent)}")); + } + + var registerResult = await _client.HttpPostAsync(_registerUrl.ToString(), jsonEvent); + + return registerResult; + } + + /// + public async Task> RegisterEventBatchAsync(IReadOnlyCollection jsonEvents) + { + if (jsonEvents.IsNullOrEmptyEnumerable()) + { + return Result + .Failure(new MessageDataModel("No events provided for registration", + $"No events provided for registration, ${nameof(jsonEvents)}")); + } + + return await RegisterEventAsync(string.Join(Environment.NewLine, jsonEvents)); + } + + /// + public IResult RegisterEvent(MLogEventDto logEvent) + => RegisterEvent(logEvent.ToString()); + + /// + public IResult RegisterEventBatch(IEnumerable logEvents) + => RegisterEventBatch(logEvents.Select(x => x.ToString()).ToList()); + + /// + public async Task> RegisterEventAsync(MLogEventDto logEvent) + => await RegisterEventAsync(logEvent.ToString()); + + /// + public async Task> RegisterEventBatchAsync(IEnumerable logEvents) + => await RegisterEventBatchAsync(logEvents.Select(x => x.ToString()).ToList()); + + /// ------------------------------------------------------------------------------------------------- + /// Initializes the required option. + /// + /// ================================================================================================= + private void InitRequired() + { + _registerUrl = new Uri(new Uri(_remoteMLogOptions.ServiceClientAddress), _remoteMLogOptions.ServiceEventRegisterPath); + +#if NET45 + _client = new HttpClient(new WebRequestHandler() + { + ClientCertificates = { _remoteMLogOptions.ServiceCertificate }, + }); + _client.Timeout = TimeSpan.FromMinutes(double.Parse(_remoteMLogOptions.ServiceTimeoutInMinute)); +#endif +#if NETSTANDARD2_0_OR_GREATER || NET || NETCOREAPP3_1_OR_GREATER + _client = new HttpClient(new HttpClientHandler { ClientCertificates = { _remoteMLogOptions.ServiceCertificate } }); + _client.Timeout = TimeSpan.FromMinutes(double.Parse(_remoteMLogOptions.ServiceTimeoutInMinute)); +#endif + } + } +} \ No newline at end of file diff --git a/src/MLogHelperDotNet/Services/MLogSoapClientService.cs b/src/MLogHelperDotNet/Services/MLogSoapClientService.cs new file mode 100644 index 0000000..523bad3 --- /dev/null +++ b/src/MLogHelperDotNet/Services/MLogSoapClientService.cs @@ -0,0 +1,73 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MLogHelperDotNet +// Author : RzR +// Created On : 2023-12-08 00:05 +// +// Last Modified By : RzR +// Last Modified On : 2023-12-19 22:02 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using AggregatedGenericResultMessage.Abstractions; +using MLogHelperDotNet.Abstractions; +using MLogHelperDotNet.Models; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously + +#endregion + +namespace MLogHelperDotNet.Services +{ + /// + public class MLogSoapClientService : IMLogEndpointClientService + { + /// ------------------------------------------------------------------------------------------------- + /// Default constructor. + /// + /// ================================================================================================= + public MLogSoapClientService() + => throw new NotImplementedException("SOAP implementation currently is not available!"); + + /// + public IResult RegisterEvent(string jsonEvent) + => throw new NotImplementedException(); + + /// + public IResult RegisterEventBatch(IReadOnlyCollection jsonEvents) + => throw new NotImplementedException(); + + /// + public async Task> RegisterEventAsync(string jsonEvent) + => throw new NotImplementedException(); + + /// + public async Task> RegisterEventBatchAsync(IReadOnlyCollection jsonEvents) + => throw new NotImplementedException(); + + /// + public IResult RegisterEvent(MLogEventDto logEvent) + => throw new NotImplementedException(); + + /// + public IResult RegisterEventBatch(IEnumerable logEvents) + => throw new NotImplementedException(); + + /// + public async Task> RegisterEventAsync(MLogEventDto logEvent) + => throw new NotImplementedException(); + + /// + public async Task> RegisterEventBatchAsync(IEnumerable logEvents) + => throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/RzR.Shared.eGovMD.sln b/src/RzR.Shared.eGovMD.sln new file mode 100644 index 0000000..79d97df --- /dev/null +++ b/src/RzR.Shared.eGovMD.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.32510.428 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MLogHelperDotNet", "MLogHelperDotNet\MLogHelperDotNet.csproj", "{1BBCAB0C-B0CB-45BB-B3D7-DDE95279B121}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{BCB2B36F-30D2-4B00-802B-52BDB4CB9E0F}" + ProjectSection(SolutionItems) = preProject + shared\GeneralAssemblyInfo.cs = shared\GeneralAssemblyInfo.cs + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E737BE4D-6DFF-4A67-A3D9-2AC58BFBBC07}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{9DF20F3B-B441-453F-9AEF-0A0AF4C0059A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebApiNet5", "tests\WebApiNet5\WebApiNet5.csproj", "{6608CC56-7286-4CCB-A024-230B83473336}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestConsoleNet45", "tests\TestConsoleNet45\TestConsoleNet45.csproj", "{B3EC20D7-C006-4B36-A042-9CCC81BEDF61}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1BBCAB0C-B0CB-45BB-B3D7-DDE95279B121}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BBCAB0C-B0CB-45BB-B3D7-DDE95279B121}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BBCAB0C-B0CB-45BB-B3D7-DDE95279B121}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BBCAB0C-B0CB-45BB-B3D7-DDE95279B121}.Release|Any CPU.Build.0 = Release|Any CPU + {6608CC56-7286-4CCB-A024-230B83473336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6608CC56-7286-4CCB-A024-230B83473336}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6608CC56-7286-4CCB-A024-230B83473336}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6608CC56-7286-4CCB-A024-230B83473336}.Release|Any CPU.Build.0 = Release|Any CPU + {B3EC20D7-C006-4B36-A042-9CCC81BEDF61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B3EC20D7-C006-4B36-A042-9CCC81BEDF61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B3EC20D7-C006-4B36-A042-9CCC81BEDF61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B3EC20D7-C006-4B36-A042-9CCC81BEDF61}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {6608CC56-7286-4CCB-A024-230B83473336} = {E737BE4D-6DFF-4A67-A3D9-2AC58BFBBC07} + {B3EC20D7-C006-4B36-A042-9CCC81BEDF61} = {E737BE4D-6DFF-4A67-A3D9-2AC58BFBBC07} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + BuildVersion_UseUniversalClock = True + BuildVersion_ConfigurationName = Release + BuildVersion_AssemblyInfoFilename = shared\GeneralAssemblyInfo.cs + BuildVersion_StartDate = 2022/8/5 + BuildVersion_UpdateFileVersion = True + BuildVersion_UpdateAssemblyVersion = True + BuildVersion_BuildVersioningStyle = None.None.Increment.TimeStamp + SolutionGuid = {4B5BD8B8-58AD-47B4-B85A-E7242D47B9FE} + EndGlobalSection +EndGlobal diff --git a/src/RzR.Shared.eGovMD.sln.DotSettings b/src/RzR.Shared.eGovMD.sln.DotSettings new file mode 100644 index 0000000..488768d --- /dev/null +++ b/src/RzR.Shared.eGovMD.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/src/shared/GeneralAssemblyInfo.cs b/src/shared/GeneralAssemblyInfo.cs new file mode 100644 index 0000000..787f91e --- /dev/null +++ b/src/shared/GeneralAssemblyInfo.cs @@ -0,0 +1,46 @@ +// *********************************************************************** +// Assembly : RzR.Shared.eGovMD.MNotifyHelperDotNet +// Author : RzR +// Created On : 2022-10-21 15:47 +// +// Last Modified By : RzR +// Last Modified On : 2022-11-17 19:11 +// *********************************************************************** +// +// Copyright (c) RzR. All rights reserved. +// +// +// +// +// *********************************************************************** + +#region U S A G E S + +using System.Reflection; +using System.Resources; + +#endregion + +#if DEBUG +[assembly: AssemblyConfiguration("Debug")] +#else +[assembly: AssemblyConfiguration("Release")] +#endif + +[assembly: AssemblyCompany("RzR ®")] +[assembly: AssemblyProduct("e-GOV MD MLogHelperDotNet")] +[assembly: AssemblyCopyright("Copyright © 2022-2023 RzR All rights reserved.")] +[assembly: AssemblyTrademark("® RzR™")] +[assembly: AssemblyDescription("A wrapper for governmental logging service for a quick way to implement it with a few configuration settings. Service is provided by an e-governance agency (https://egov.md/), named `MLog`, available in the Republic of Moldova.")] + +[assembly: AssemblyMetadata("TermsOfService", "")] + +[assembly: AssemblyMetadata("ContactUrl", "")] +[assembly: AssemblyMetadata("ContactName", "RzR")] +[assembly: AssemblyMetadata("ContactEmail", "ddpRzR@hotmail.com")] + +[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)] + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyInformationalVersion("1.0.0.0")] diff --git a/src/tests/TestConsoleNet45/App.config b/src/tests/TestConsoleNet45/App.config new file mode 100644 index 0000000..b17e988 --- /dev/null +++ b/src/tests/TestConsoleNet45/App.config @@ -0,0 +1,24 @@ + + + + +
+ + + + + + + + + + + + \ No newline at end of file diff --git a/src/tests/TestConsoleNet45/App.xsd b/src/tests/TestConsoleNet45/App.xsd new file mode 100644 index 0000000..9ececbd --- /dev/null +++ b/src/tests/TestConsoleNet45/App.xsd @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/tests/TestConsoleNet45/Program.cs b/src/tests/TestConsoleNet45/Program.cs new file mode 100644 index 0000000..2c77f09 --- /dev/null +++ b/src/tests/TestConsoleNet45/Program.cs @@ -0,0 +1,34 @@ +using System; +using MLogHelperDotNet.Models; +using MLogHelperDotNet.Services; + +namespace TestConsoleNet45 +{ + class Program + { + static void Main(string[] args) + { + var logInstance = new MLogRestClientService(); + + var opt = new RemoteMLogOptions() + { + ServiceTimeoutInMinute = "1", + ServiceCertificatePassword = "pass", + ServiceClientAddress = "https://lg.lg", + ServiceCertificatePath = "temp/app" + }; + var logInstance1 = new MLogRestClientService(ref opt); + + var log = logInstance.RegisterEvent(new MLogEventDto() + { + EventCorrelation = Guid.NewGuid().ToString(), + EventID = Guid.NewGuid().ToString(), + EventTime = DateTime.Now, + Subject = "test" + }); + + Console.WriteLine(log.Response); + Console.ReadKey(); + } + } +} diff --git a/src/tests/TestConsoleNet45/Properties/AssemblyInfo.cs b/src/tests/TestConsoleNet45/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..37aa432 --- /dev/null +++ b/src/tests/TestConsoleNet45/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("TestConsoleNet45")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("TestConsoleNet45")] +[assembly: AssemblyCopyright("Copyright © 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b3ec20d7-c006-4b36-a042-9ccc81bedf61")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/tests/TestConsoleNet45/TestConsoleNet45.csproj b/src/tests/TestConsoleNet45/TestConsoleNet45.csproj new file mode 100644 index 0000000..0adb65d --- /dev/null +++ b/src/tests/TestConsoleNet45/TestConsoleNet45.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {B3EC20D7-C006-4B36-A042-9CCC81BEDF61} + Exe + TestConsoleNet45 + TestConsoleNet45 + v4.5 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\MLogHelperDotNet\bin\Debug\net45\AggregatedGenericResultMessage.dll + + + ..\..\MLogHelperDotNet\bin\Debug\net45\CodeSource.dll + + + ..\..\MLogHelperDotNet\bin\Debug\net45\DomainCommonExtensions.dll + + + ..\..\MLogHelperDotNet\bin\Debug\net45\EntityMaxLengthTrim.dll + + + ..\..\MLogHelperDotNet\bin\Debug\net45\MLogHelperDotNet.dll + + + + + ..\..\MLogHelperDotNet\bin\Debug\net45\System.Linq.Dynamic.Core.dll + + + False + ..\..\MLogHelperDotNet\bin\Debug\net45\System.ValueTuple.dll + + + + + + + + + + + + + + + + Designer + + + + + + + \ No newline at end of file diff --git a/src/tests/WebApiNet5/Controllers/WeatherForecastController.cs b/src/tests/WebApiNet5/Controllers/WeatherForecastController.cs new file mode 100644 index 0000000..352f584 --- /dev/null +++ b/src/tests/WebApiNet5/Controllers/WeatherForecastController.cs @@ -0,0 +1,64 @@ +using Microsoft.AspNetCore.Mvc; +using System; +using System.Collections.Generic; +using AggregatedGenericResultMessage.Web; +using MLogHelperDotNet.Abstractions; +using MLogHelperDotNet.Enums; +using MLogHelperDotNet.Models; + +namespace WebApiNet5.Controllers +{ + [ApiController] + [Route("[controller]/[action]")] + public class WeatherForecastController : ResultBaseApiController + { + /// + /// Client factory + /// + private readonly Func _clientFactory; + + public WeatherForecastController(Func clientFactory) + { + _clientFactory = clientFactory; + } + + [HttpPost] + public IActionResult SendSoap() + { + var endpoint = _clientFactory(EndpointType.SOAP); + var result = endpoint.RegisterEvent("Test"); + + return JsonResult(result); + } + + [HttpPost] + public IActionResult SendRest() + { + var endpoint = _clientFactory(EndpointType.REST); + var result = endpoint.RegisterEvent("Test"); + + return JsonResult(result); + } + + [HttpPost] + public IActionResult SendRest1() + { + var endpoint = _clientFactory(EndpointType.REST); + var result = endpoint.RegisterEvent(new MLogEventDto() + { + User = "testUser", + EventCorrelation = Guid.Empty.ToString(), + EventID = Guid.NewGuid().ToString(), + EventTime = DateTime.Now, + EventType = "Info", + EventDetails = new List() { 1, 3, 5 }, + Object = new Dictionary() + { + {"1.1", 0.2M}, {"1.2", 0.3M} + } + }); + + return JsonResult(result); + } + } +} diff --git a/src/tests/WebApiNet5/Program.cs b/src/tests/WebApiNet5/Program.cs new file mode 100644 index 0000000..5bb3ae0 --- /dev/null +++ b/src/tests/WebApiNet5/Program.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Hosting; + +namespace WebApiNet5 +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/src/tests/WebApiNet5/Properties/launchSettings.json b/src/tests/WebApiNet5/Properties/launchSettings.json new file mode 100644 index 0000000..f474b69 --- /dev/null +++ b/src/tests/WebApiNet5/Properties/launchSettings.json @@ -0,0 +1,31 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:46566", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WebApiNet5": { + "commandName": "Project", + "dotnetRunMessages": "true", + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/tests/WebApiNet5/Startup.cs b/src/tests/WebApiNet5/Startup.cs new file mode 100644 index 0000000..63ecfc0 --- /dev/null +++ b/src/tests/WebApiNet5/Startup.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.OpenApi.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using MLogHelperDotNet; + +namespace WebApiNet5 +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApiNet5", Version = "v1" }); + }); + + services.AddMLogService(Configuration); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApiNet5 v1")); + } + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/src/tests/WebApiNet5/WebApiNet5.csproj b/src/tests/WebApiNet5/WebApiNet5.csproj new file mode 100644 index 0000000..deefc22 --- /dev/null +++ b/src/tests/WebApiNet5/WebApiNet5.csproj @@ -0,0 +1,16 @@ + + + + net5.0 + + + + + + + + + + + + diff --git a/src/tests/WebApiNet5/appsettings.Development.json b/src/tests/WebApiNet5/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/src/tests/WebApiNet5/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/tests/WebApiNet5/appsettings.json b/src/tests/WebApiNet5/appsettings.json new file mode 100644 index 0000000..ef4d407 --- /dev/null +++ b/src/tests/WebApiNet5/appsettings.json @@ -0,0 +1,19 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*", + + "RemoteMLogOptions": { + "ServiceClientAddress": "https://test.log.lg", + "ServiceEventRegisterPath": "register", + "ServiceEventGetPath": "query", + "ServiceCertificatePath": "QT2.pfx", + "ServiceCertificatePassword": "QT2", + "ServiceTimeoutInMinute": "10" + } +}