├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Common ├── Common.csproj ├── Contracts │ ├── ConnectorApiActions.cs │ ├── ContractEnums.cs │ ├── DataJobStatus.cs │ ├── DataJobStatusDetail.cs │ ├── DataMessage.cs │ ├── DequeueResponse.cs │ ├── EntityExecutionStatus.cs │ ├── PackageApiActions.cs │ └── SettingsConstants.cs ├── Helpers │ ├── AuthenticationHelper.cs │ ├── EncryptDecrypt.cs │ ├── Extensions.cs │ ├── FileOperationsHelper.cs │ ├── HttpClientHelper.cs │ └── HttpRetryHandler.cs ├── JobSettings │ ├── DownloadJobSettings.cs │ ├── ExecutionJobSettings.cs │ ├── ExportJobSettings.cs │ ├── ImportJobSettings.cs │ ├── ProcessingJobSettings.cs │ ├── Settings.cs │ └── UploadJobSettings.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx └── app.config ├── Job.Download ├── Download.cs ├── Job.Download.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── app.config └── packages.config ├── Job.ExecutionMonitor ├── ExecutionMonitor.cs ├── Job.ExecutionMonitor.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── app.config └── packages.config ├── Job.Export ├── Export.cs ├── Job.Export.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── app.config └── packages.config ├── Job.Import ├── Import.cs ├── Job.Import.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── app.config └── packages.config ├── Job.ProcessingMonitor ├── Job.ProcessingMonitor.csproj ├── ProcessingMonitor.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── app.config └── packages.config ├── Job.Upload ├── Job.Upload.csproj ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── Upload.cs ├── app.config └── packages.config ├── LICENSE.txt ├── README.md ├── Recurring Integrations Scheduler.sln ├── SECURITY.md ├── Scheduler ├── App.config ├── Assets │ ├── Add_16xMD.png │ ├── Edit_16xMD.png │ ├── Folder open_32xMD_exp.png │ ├── Recurring Integrations Scheduler.ico │ ├── Remove_16xMD.png │ └── ValidateDocument_16x.png ├── Forms │ ├── AadApplicationForm.Designer.cs │ ├── AadApplicationForm.cs │ ├── AadApplicationForm.resx │ ├── AboutBox.Designer.cs │ ├── AboutBox.cs │ ├── AboutBox.resx │ ├── Connect.Designer.cs │ ├── Connect.cs │ ├── Connect.resx │ ├── CronExamples.Designer.cs │ ├── CronExamples.cs │ ├── CronExamples.resx │ ├── DataJobForm.Designer.cs │ ├── DataJobForm.cs │ ├── DataJobForm.resx │ ├── DownloadJobV3.Designer.cs │ ├── DownloadJobV3.cs │ ├── DownloadJobV3.resx │ ├── ExportJobV3.Designer.cs │ ├── ExportJobV3.cs │ ├── ExportJobV3.resx │ ├── ImportJobV3.Designer.cs │ ├── ImportJobV3.cs │ ├── ImportJobV3.resx │ ├── InstanceForm.Designer.cs │ ├── InstanceForm.cs │ ├── InstanceForm.resx │ ├── JobGroupForm.Designer.cs │ ├── JobGroupForm.cs │ ├── JobGroupForm.resx │ ├── MainForm.Designer.cs │ ├── MainForm.cs │ ├── MainForm.resx │ ├── Parameters.Designer.cs │ ├── Parameters.cs │ ├── Parameters.resx │ ├── UploadJobV3.Designer.cs │ ├── UploadJobV3.cs │ ├── UploadJobV3.resx │ ├── UserForm.Designer.cs │ ├── UserForm.cs │ ├── UserForm.resx │ ├── ValidateConnection.Designer.cs │ ├── ValidateConnection.cs │ └── ValidateConnection.resx ├── FormsHelper.cs ├── GlobalSuppressions.cs ├── Program.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Scheduler.cs ├── Scheduler.csproj ├── Settings │ └── Settings.cs ├── app.manifest └── packages.config ├── Server ├── App.config ├── Configuration.cs ├── IQuartzServer.cs ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── QuartzServer.cs ├── QuartzServerFactory.cs ├── Schedule.xml ├── Server.csproj └── packages.config ├── Setup ├── README.md ├── Recurring Integrations Scheduler.ico └── Recurring Integrations Scheduler.iss ├── Third Party Notices.txt └── Version.cs /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Recurring Integration Scheduler version (please complete the following information):** 27 | - Version [e.g. 22] 28 | 29 | **Dynamics 365 version (please complete the following information):** 30 | - Application version 31 | - Platform version 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ## Types of changes 2 | 3 | - [ ] Bug fix (non-breaking change which fixes an issue) 4 | - [ ] New feature (non-breaking change which adds functionality) 5 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 6 | - [ ] I have read the **CONTRIBUTING** document. 7 | - [ ] My code follows the code style of this project. 8 | - [ ] My change requires a change to the documentation. 9 | - [ ] I have updated the documentation accordingly. 10 | - [ ] I have added tests to cover my changes. 11 | - [ ] All new and existing tests passed. 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | # tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | 290 | # Build artefacts 291 | /.vscode 292 | /Setup/Quartz.Server.exe 293 | /Setup/Topshelf.dll 294 | /Recurring Integrations Scheduler Setup.exe 295 | /Setup/Quartz.Serialization.Json.dll 296 | /Setup/Quartz.Plugins.dll 297 | /Setup/Quartz.Jobs.dll 298 | /Setup/Quartz.dll 299 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | [homepage]: http://contributor-covenant.org 3 | [version]: http://contributor-covenant.org/version/1/4/ 4 | 5 | 6 | # Microsoft Open Source Code of Conduct 7 | This code of conduct outlines expectations for participation in Microsoft-managed open source communities, as well as steps for reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all. People violating this code of conduct may be banned from the community. 8 | 9 | Our open source communities strive to: 10 | * **Be friendly and patient:** Remember you might not be communicating in someone else's primary spoken or programming language, and others may not have your level of understanding. 11 | * **Be welcoming:** Our communities welcome and support people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, color, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability. 12 | * **Be respectful:** We are a world-wide community of professionals, and we conduct ourselves professionally. Disagreement is no excuse for poor behavior and poor manners. Disrespectful and unacceptable behavior includes, but is not limited to: 13 | * Violent threats or language. 14 | * Discriminatory or derogatory jokes and language. 15 | * Posting sexually explicit or violent material. 16 | * Posting, or threatening to post, people's personally identifying information ("doxing"). 17 | * Insults, especially those using discriminatory terms or slurs. 18 | * Behavior that could be perceived as sexual attention. 19 | * Advocating for or encouraging any of the above behaviors. 20 | * **Understand disagreements:** Disagreements, both social and technical, are useful learning opportunities. Seek to understand the other viewpoints and resolve differences constructively. 21 | * This code is not exhaustive or complete. It serves to capture our common understanding of a productive, collaborative environment. We expect the code to be followed in spirit as much as in the letter. 22 | ## Scope 23 | This code of conduct applies to all repos and communities for Microsoft-managed open source projects regardless of whether or not the repo explicitly calls out its use of this code. The code also applies in public spaces when an individual is representing a project or its community. Examples include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 24 | 25 | Note: Some Microsoft-managed communities have codes of conduct that pre-date this document and issue resolution process. While communities are not required to change their code, they are expected to use the resolution process outlined here. The review team will coordinate with the communities involved to address your concerns. 26 | ## Reporting Code of Conduct Issues 27 | We encourage all communities to resolve issues on their own whenever possible. This builds a broader and deeper understanding and ultimately a healthier interaction. In the event that an issue cannot be resolved locally, please feel free to report your concerns by contacting [opencode@microsoft.com](mailto:opencode@microsoft.com). Your report will be handled in accordance with the issue resolution process described in the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/). 28 | 29 | In your report please include: 30 | * Your contact information. 31 | * Names (real, usernames or pseudonyms) of any individuals involved. If there are additional witnesses, please include them as well. 32 | * Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly available record (e.g. a mailing list archive or a public chat log), please include a link or attachment. 33 | * Any additional information that may be helpful. 34 | 35 | All reports will be reviewed by a multi-person team and will result in a response that is deemed necessary and appropriate to the circumstances. Where additional perspectives are needed, the team may seek insight from others with relevant expertise or experience. The confidentiality of the person reporting the incident will be kept at all times. Involved parties are never part of the review team. 36 | 37 | Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable behavior, the review team may take any action they deem appropriate, including a permanent ban from the community. 38 | 39 | _This code of conduct is based on the [template](http://todogroup.org/opencodeofconduct) established by the TODO Group and used by numerous other large communities (e.g., [Facebook](https://code.facebook.com/pages/876921332402685/open-source-code-of-conduct), [Yahoo](https://yahoo.github.io/codeofconduct), [Twitter](https://engineering.twitter.com/opensource/code-of-conduct), [GitHub](http://todogroup.org/opencodeofconduct/#opensource@github.com)) and the Scope section from the [Contributor Covenant version 1.4](http://contributor-covenant.org/version/1/4/)._ 40 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. 4 | 5 | When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA. 6 | 7 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 8 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 9 | -------------------------------------------------------------------------------- /Common/Common.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {96BB041A-3FDE-400F-ADDA-9DB19F3274BE} 8 | Library 9 | Properties 10 | RecurringIntegrationsScheduler.Common 11 | RecurringIntegrationsScheduler.Common 12 | v4.7.2 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | ..\Output\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | false 25 | false 26 | 8.0 27 | 28 | 29 | pdbonly 30 | true 31 | ..\Output\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 8.0 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | Version.cs 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | True 78 | True 79 | Resources.resx 80 | 81 | 82 | 83 | 84 | ResXFileCodeGenerator 85 | Resources.Designer.cs 86 | Designer 87 | 88 | 89 | 90 | 91 | 2.0.13 92 | 93 | 94 | 4.37.0 95 | 96 | 97 | 5.2.9 98 | 99 | 100 | 13.0.1 101 | 102 | 103 | 7.2.2 104 | 105 | 106 | 3.3.3 107 | 108 | 109 | 1.2.14 110 | 111 | 112 | 2.0.0 113 | 114 | 115 | 116 | 123 | -------------------------------------------------------------------------------- /Common/Contracts/ConnectorApiActions.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | namespace RecurringIntegrationsScheduler.Common.Contracts 5 | { 6 | /// 7 | /// Class holding all requests paths related to Connector API 8 | /// 9 | public static class ConnectorApiActions 10 | { 11 | public const string EnqueuePath = "api/connector/enqueue/"; 12 | public const string DequeuePath = "api/connector/dequeue/"; 13 | public const string AckPath = "api/connector/ack/"; 14 | public const string JobStatusPath = "api/connector/jobstatus/"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Common/Contracts/ContractEnums.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | namespace RecurringIntegrationsScheduler.Common.Contracts 5 | { 6 | /// 7 | /// Recurring data job state 8 | /// 9 | public enum DataJobState 10 | { 11 | Enqueued = 0, 12 | Dequeued = 1, 13 | Downloaded = 2, 14 | Acked = 3, 15 | InProcess = 4, 16 | Processed = 5, 17 | ProcessedWithErrors = 6, 18 | PostProcessError = 7, 19 | PreProcessError = 8 20 | } 21 | 22 | /// 23 | /// Entity execution status 24 | /// 25 | public enum EntityExecutionStateEnum 26 | { 27 | NotStarted, 28 | Waiting, 29 | Executing, 30 | Error, 31 | Finished, 32 | Ready, 33 | NotRun, 34 | Cancelling, 35 | Canceled, 36 | Hold 37 | } 38 | 39 | /// 40 | /// Message status 41 | /// 42 | public enum MessageStatus 43 | { 44 | Input, 45 | InProcess, 46 | Enqueued, 47 | Failed, 48 | Succeeded 49 | } 50 | 51 | /// 52 | /// Order by options 53 | /// 54 | public enum OrderByOptions 55 | { 56 | Created, 57 | Modified, 58 | FileName, 59 | Size 60 | } 61 | 62 | /// 63 | /// IntegrationActivityMessageStatus 64 | /// 65 | public enum IntegrationActivityMessageStatus 66 | { 67 | Enqueued = 0, 68 | Dequeued = 1, 69 | Downloaded = 2, 70 | Acked = 3, 71 | Processing = 4, 72 | Processed = 5, 73 | ProcessedWithErrors = 6, 74 | PostProcessingError = 7, 75 | PreProcessingError = 8, 76 | PreProcessing = 9 77 | } 78 | } -------------------------------------------------------------------------------- /Common/Contracts/DataJobStatus.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System; 5 | 6 | namespace RecurringIntegrationsScheduler.Common.Contracts 7 | { 8 | /// 9 | /// Recurring data job status object 10 | /// 11 | public class DataJobStatus 12 | { 13 | /// 14 | /// Gets or sets the data project. 15 | /// 16 | /// 17 | /// The data project. 18 | /// 19 | public string DataProject { get; set; } 20 | /// 21 | /// Gets or sets the data job identifier. 22 | /// 23 | /// 24 | /// The data job identifier. 25 | /// 26 | public string DataJobIdentifier { get; set; } 27 | /// 28 | /// Gets or sets the external identifier. 29 | /// 30 | /// 31 | /// The external identifier. 32 | /// 33 | public string ExternalIdentifier { get; set; } 34 | /// 35 | /// Gets or sets the execution identifier. 36 | /// 37 | /// 38 | /// The execution identifier. 39 | /// 40 | public string ExecutionIdentifier { get; set; } 41 | /// 42 | /// Gets or sets the data job started date time. 43 | /// 44 | /// 45 | /// The data job started date time. 46 | /// 47 | public DateTime DataJobStartedDateTime { get; set; } 48 | /// 49 | /// Gets or sets the data job completed date time. 50 | /// 51 | /// 52 | /// The data job completed date time. 53 | /// 54 | public DateTime DataJobCompletedDateTime { get; set; } 55 | /// 56 | /// Gets or sets the state of the data job. 57 | /// 58 | /// 59 | /// The state of the data job. 60 | /// 61 | public DataJobState DataJobState { get; set; } 62 | } 63 | } -------------------------------------------------------------------------------- /Common/Contracts/DataJobStatusDetail.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Collections.Generic; 5 | 6 | namespace RecurringIntegrationsScheduler.Common.Contracts 7 | { 8 | /// 9 | /// Recurring data job status detail object 10 | /// 11 | public class DataJobStatusDetail 12 | { 13 | /// 14 | /// The data job status 15 | /// 16 | public DataJobStatus DataJobStatus; 17 | 18 | /// 19 | /// The execution log 20 | /// 21 | public string ExecutionLog; 22 | 23 | /// 24 | /// Gets or sets the execution detail. 25 | /// 26 | /// 27 | /// The execution detail. 28 | /// 29 | public List ExecutionDetail { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /Common/Contracts/DataMessage.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using Newtonsoft.Json; 5 | using Newtonsoft.Json.Converters; 6 | 7 | namespace RecurringIntegrationsScheduler.Common.Contracts 8 | { 9 | /// 10 | /// Contract to abstract the input data file 11 | /// and its processing status (on the client) 12 | /// 13 | public class DataMessage 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | public DataMessage() 19 | { 20 | } 21 | 22 | /// 23 | /// Initiate new dataMessage object based on existing one 24 | /// 25 | /// 26 | public DataMessage(DataMessage dataMessage) 27 | { 28 | Name = dataMessage.Name; 29 | FullPath = dataMessage.FullPath; 30 | MessageId = dataMessage.MessageId; 31 | MessageStatus = dataMessage.MessageStatus; 32 | DataJobState = dataMessage.DataJobState; 33 | CorrelationId = dataMessage.CorrelationId; 34 | PopReceipt = dataMessage.PopReceipt; 35 | DownloadLocation = dataMessage.DownloadLocation; 36 | } 37 | 38 | /// 39 | /// Name. 40 | /// 41 | /// 42 | /// The name. 43 | /// 44 | public string Name { get; set; } 45 | 46 | /// 47 | /// Full path of the message 48 | /// 49 | /// 50 | /// The full path. 51 | /// 52 | public string FullPath { get; set; } 53 | 54 | /// 55 | /// Id of the message in data job queue 56 | /// 57 | /// 58 | /// The message identifier. 59 | /// 60 | public string MessageId { get; set; } 61 | 62 | /// 63 | /// Status 64 | /// 65 | /// 66 | /// The message status. 67 | /// 68 | [JsonConverter(typeof(StringEnumConverter))] 69 | public MessageStatus MessageStatus { get; set; } 70 | 71 | /// 72 | /// Data job state 73 | /// 74 | /// 75 | /// The state of the data job. 76 | /// 77 | [JsonConverter(typeof(StringEnumConverter))] 78 | public DataJobState DataJobState { get; set; } 79 | 80 | /// 81 | /// Gets or sets the correlation identifier. 82 | /// 83 | /// 84 | /// The correlation identifier. 85 | /// 86 | public string CorrelationId { get; set; } 87 | 88 | /// 89 | /// Gets or sets the pop receipt. 90 | /// 91 | /// 92 | /// The pop receipt. 93 | /// 94 | public string PopReceipt { get; set; } 95 | 96 | /// 97 | /// Gets or sets the download location. 98 | /// 99 | /// 100 | /// The download location. 101 | /// 102 | public string DownloadLocation { get; set; } 103 | } 104 | } -------------------------------------------------------------------------------- /Common/Contracts/DequeueResponse.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | namespace RecurringIntegrationsScheduler.Common.Contracts 5 | { 6 | public class DequeueResponse 7 | { 8 | /// 9 | /// Gets or sets the correlation identifier. 10 | /// 11 | /// 12 | /// The correlation identifier. 13 | /// 14 | public string CorrelationId { get; set; } 15 | 16 | /// 17 | /// Gets or sets the pop receipt. 18 | /// 19 | /// 20 | /// The pop receipt. 21 | /// 22 | public string PopReceipt { get; set; } 23 | 24 | /// 25 | /// Gets or sets the download location. 26 | /// 27 | /// 28 | /// The download location. 29 | /// 30 | public string DownloadLocation { get; set; } 31 | 32 | /// 33 | /// Converter from DataMessage 34 | /// 35 | /// The data message. 36 | /// 37 | /// The result of the conversion. 38 | /// 39 | public static explicit operator DequeueResponse(DataMessage dataMessage) 40 | { 41 | return new DequeueResponse 42 | { 43 | CorrelationId = dataMessage.CorrelationId, 44 | PopReceipt = dataMessage.PopReceipt, 45 | DownloadLocation = dataMessage.DownloadLocation 46 | }; 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Common/Contracts/EntityExecutionStatus.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System; 5 | 6 | namespace RecurringIntegrationsScheduler.Common.Contracts 7 | { 8 | public class EntityExecutionStatus 9 | { 10 | /// 11 | /// Gets or sets the name of the entity. 12 | /// 13 | /// 14 | /// The name of the entity. 15 | /// 16 | public string EntityName { get; set; } 17 | /// 18 | /// Gets or sets the company. 19 | /// 20 | /// 21 | /// The company. 22 | /// 23 | public string Company { get; set; } 24 | /// 25 | /// Gets or sets the staging records. 26 | /// 27 | /// 28 | /// The staging records. 29 | /// 30 | public int StagingRecords { get; set; } 31 | /// 32 | /// Gets or sets the staging status. 33 | /// 34 | /// 35 | /// The staging status. 36 | /// 37 | public EntityExecutionStateEnum StagingStatus { get; set; } 38 | /// 39 | /// Gets or sets the staging error count. 40 | /// 41 | /// 42 | /// The staging error count. 43 | /// 44 | public int StagingErrorCount { get; set; } 45 | /// 46 | /// Gets or sets the target records. 47 | /// 48 | /// 49 | /// The target records. 50 | /// 51 | public int TargetRecords { get; set; } 52 | /// 53 | /// Gets or sets the target status. 54 | /// 55 | /// 56 | /// The target status. 57 | /// 58 | public EntityExecutionStateEnum TargetStatus { get; set; } 59 | /// 60 | /// Gets or sets the target error count. 61 | /// 62 | /// 63 | /// The target error count. 64 | /// 65 | public int TargetErrorCount { get; set; } 66 | /// 67 | /// Gets or sets the execution started date time. 68 | /// 69 | /// 70 | /// The execution started date time. 71 | /// 72 | public DateTime ExecutionStartedDateTime { get; set; } 73 | /// 74 | /// Gets or sets the execution completed date time. 75 | /// 76 | /// 77 | /// The execution completed date time. 78 | /// 79 | public DateTime ExecutionCompletedDateTime { get; set; } 80 | } 81 | } -------------------------------------------------------------------------------- /Common/Contracts/PackageApiActions.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | namespace RecurringIntegrationsScheduler.Common.Contracts 5 | { 6 | /// 7 | /// Class holding requests paths related to Package API 8 | /// 9 | public static class PackageApiActions 10 | { 11 | public const string GetAzureWriteUrlActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetAzureWriteUrl"; 12 | public const string GetExecutionSummaryStatusActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetExecutionSummaryStatus"; 13 | public const string GetExportedPackageUrlActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetExportedPackageUrl"; 14 | public const string GetExecutionSummaryPageUrlActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetExecutionSummaryPageUrl"; 15 | public const string ImportFromPackageActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.ImportFromPackage"; 16 | public const string DeleteExecutionHistoryJobActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.DeleteExecutionHistoryJob"; 17 | public const string ExportToPackageActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.ExportToPackage"; 18 | public const string ExportFromPackageActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.ExportFromPackage"; 19 | public const string GetMessageStatusActionPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetMessageStatus"; 20 | public const string GetImportTargetErrorKeysFileUrlPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetImportTargetErrorKeysFileUrl"; 21 | public const string GenerateImportTargetErrorKeysFilePath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GenerateImportTargetErrorKeysFile"; 22 | public const string GetExecutionErrorsPath = "data/DataManagementDefinitionGroups/Microsoft.Dynamics.DataEntities.GetExecutionErrors"; 23 | } 24 | } -------------------------------------------------------------------------------- /Common/Helpers/AuthenticationHelper.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using Microsoft.Identity.Client; 5 | using Microsoft.IdentityModel.Clients.ActiveDirectory; 6 | using RecurringIntegrationsScheduler.Common.JobSettings; 7 | using System; 8 | using System.Linq; 9 | using System.Security; 10 | using System.Threading.Tasks; 11 | using UrlCombineLib; 12 | 13 | namespace RecurringIntegrationsScheduler.Common.Helpers 14 | { 15 | /// 16 | /// Authentication helper class 17 | /// 18 | internal class AuthenticationHelper 19 | { 20 | private readonly Settings _settings; 21 | private string _authorizationHeader; 22 | 23 | /// 24 | /// Initializes a new instance of the class. 25 | /// 26 | /// Job settings 27 | public AuthenticationHelper(Settings jobSettings) 28 | { 29 | _settings = jobSettings; 30 | } 31 | 32 | /// 33 | /// Authorization result property. 34 | /// 35 | /// 36 | /// Authentication result. 37 | /// 38 | private Microsoft.Identity.Client.AuthenticationResult AuthenticationResult { get; set; } 39 | private Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AuthenticationResultADAL { get; set; } 40 | 41 | /// 42 | /// Sets authorization header. 43 | /// 44 | /// Authorization header 45 | private async Task AuthorizationHeader() 46 | { 47 | if (!string.IsNullOrEmpty(_authorizationHeader) && (DateTime.UtcNow.AddSeconds(60) < AuthenticationResult.ExpiresOn)) 48 | { 49 | return _authorizationHeader; 50 | } 51 | IConfidentialClientApplication appConfidential; 52 | IPublicClientApplication appPublic; 53 | var aosUriAuthUri = new Uri(_settings.AosUri); 54 | string authority = UrlCombine.Combine(_settings.AzureAuthEndpoint, _settings.AadTenant); 55 | string[] scopes = new string[] { UrlCombine.Combine(aosUriAuthUri.AbsoluteUri, ".default") }; 56 | 57 | if (_settings.UseServiceAuthentication) 58 | { 59 | appConfidential = ConfidentialClientApplicationBuilder.Create(_settings.AadClientId.ToString()) 60 | .WithClientSecret(_settings.AadClientSecret) 61 | .WithAuthority(authority) 62 | .Build(); 63 | AuthenticationResult = await appConfidential.AcquireTokenForClient(scopes).ExecuteAsync(); 64 | } 65 | else 66 | { 67 | appPublic = PublicClientApplicationBuilder.Create(_settings.AadClientId.ToString()) 68 | .WithAuthority(authority) 69 | .Build(); 70 | var accounts = await appPublic.GetAccountsAsync(); 71 | 72 | if (accounts.Any()) 73 | { 74 | AuthenticationResult = await appPublic.AcquireTokenSilent(scopes, accounts.FirstOrDefault()).ExecuteAsync(); 75 | } 76 | else 77 | { 78 | using var securePassword = new SecureString(); 79 | foreach (char c in _settings.UserPassword) 80 | { 81 | securePassword.AppendChar(c); 82 | } 83 | AuthenticationResult = await appPublic.AcquireTokenByUsernamePassword(scopes, _settings.UserName, securePassword).ExecuteAsync(); 84 | } 85 | } 86 | return _authorizationHeader = AuthenticationResult.CreateAuthorizationHeader(); 87 | } 88 | 89 | private async Task AuthorizationHeaderADAL() 90 | { 91 | if (!string.IsNullOrEmpty(_authorizationHeader) && 92 | (DateTime.UtcNow.AddSeconds(60) < AuthenticationResultADAL.ExpiresOn)) return _authorizationHeader; 93 | 94 | var uri = new UriBuilder(_settings.AzureAuthEndpoint) 95 | { 96 | Path = _settings.AadTenant 97 | }; 98 | 99 | var aosUriAuthUri = new Uri(_settings.AosUri); 100 | string aosUriAuth = aosUriAuthUri.GetLeftPart(UriPartial.Authority); 101 | 102 | var authenticationContext = new AuthenticationContext(uri.ToString(), validateAuthority: false); 103 | 104 | if (_settings.UseServiceAuthentication) 105 | { 106 | var credentials = new Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential(_settings.AadClientId.ToString(), _settings.AadClientSecret); 107 | 108 | AuthenticationResultADAL = await authenticationContext.AcquireTokenAsync(aosUriAuth, credentials); 109 | } 110 | else 111 | { 112 | var credentials = new UserPasswordCredential(_settings.UserName, _settings.UserPassword); 113 | 114 | AuthenticationResultADAL = await authenticationContext.AcquireTokenAsync(aosUriAuth, _settings.AadClientId.ToString(), credentials); 115 | } 116 | 117 | return _authorizationHeader = AuthenticationResultADAL.CreateAuthorizationHeader(); 118 | } 119 | 120 | /// 121 | /// Gets valid authentication header 122 | /// 123 | /// 124 | /// string 125 | /// 126 | public async Task GetValidAuthenticationHeader() 127 | { 128 | if (_settings.UseADAL) 129 | { 130 | _authorizationHeader = await AuthorizationHeaderADAL(); 131 | } 132 | else 133 | { 134 | _authorizationHeader = await AuthorizationHeader(); 135 | } 136 | return _authorizationHeader; 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /Common/Helpers/EncryptDecrypt.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System; 5 | using System.Security.Cryptography; 6 | using System.Text; 7 | 8 | namespace RecurringIntegrationsScheduler.Common.Helpers 9 | { 10 | public static class EncryptDecrypt 11 | { 12 | /// 13 | /// Decrypts a given string. 14 | /// 15 | /// A base64 encoded string that was created 16 | /// through the or 17 | /// extension methods. 18 | /// 19 | /// The decrypted string. 20 | /// 21 | /// If 22 | /// is a null reference. 23 | /// 24 | /// Keep in mind that the decrypted string remains in memory 25 | /// and makes your application vulnerable per se. If runtime protection 26 | /// is essential, should be used. 27 | /// 28 | public static string Decrypt(string cipher) 29 | { 30 | try 31 | { 32 | //parse base64 string 33 | var data = Convert.FromBase64String(cipher); 34 | 35 | //decrypt data 36 | var decrypted = ProtectedData.Unprotect(data, null, DataProtectionScope.LocalMachine); 37 | return Encoding.Unicode.GetString(decrypted); 38 | } 39 | catch 40 | { 41 | return string.Empty; 42 | } 43 | } 44 | 45 | /// 46 | /// Encrypts a given password and returns the encrypted data 47 | /// as a base64 string. 48 | /// 49 | /// An unencrypted string that needs 50 | /// to be secured. 51 | /// 52 | /// A base64 encoded string that represents the encrypted 53 | /// binary data. 54 | /// 55 | /// plainText 56 | /// If 57 | /// is a null reference. 58 | /// 59 | /// This solution is not really secure as we are 60 | /// keeping strings in memory. If runtime protection is essential, 61 | /// should be used. 62 | /// 63 | public static string Encrypt(string plainText) 64 | { 65 | if (plainText == null) throw new ArgumentNullException(nameof(plainText)); 66 | try 67 | { 68 | //encrypt data 69 | var data = Encoding.Unicode.GetBytes(plainText); 70 | var encrypted = ProtectedData.Protect(data, null, DataProtectionScope.LocalMachine); 71 | 72 | //return as base64 string 73 | return Convert.ToBase64String(encrypted); 74 | } 75 | catch 76 | { 77 | return string.Empty; 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /Common/Helpers/Extensions.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Collections; 5 | using System.Linq; 6 | using System.Linq.Dynamic.Core; 7 | 8 | namespace RecurringIntegrationsScheduler.Common.Helpers 9 | { 10 | public static class Extensions 11 | { 12 | /// 13 | /// Sort extension 14 | /// 15 | /// IEnumerable collection. 16 | /// SortBy keyword 17 | /// order 18 | /// 19 | public static IEnumerable Sort(this IQueryable collection, string sortBy, bool reverse = false) 20 | { 21 | return collection.OrderBy(sortBy + (reverse ? " descending" : "")); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Common/Helpers/HttpRetryHandler.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using log4net; 5 | using RecurringIntegrationsScheduler.Common.JobSettings; 6 | using System; 7 | using System.Linq; 8 | using System.Net.Http; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace RecurringIntegrationsScheduler.Common.Helpers 13 | { 14 | public class HttpRetryHandler : DelegatingHandler 15 | { 16 | private int _retryAfter; 17 | private readonly Settings _settings; 18 | /// 19 | /// The log 20 | /// 21 | private static readonly ILog Log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 22 | 23 | public HttpRetryHandler(HttpMessageHandler innerHandler, Settings jobSettings) 24 | : base(innerHandler) 25 | { 26 | _settings = jobSettings; 27 | _retryAfter = _settings.RetryDelay; 28 | log4net.Config.XmlConfigurator.Configure(); 29 | } 30 | 31 | protected override async Task SendAsync( 32 | HttpRequestMessage request, 33 | CancellationToken cancellationToken) 34 | { 35 | HttpResponseMessage response = null; 36 | for (int i = 0; i < _settings.RetryCount; i++) 37 | { 38 | response = await base.SendAsync(request, cancellationToken); 39 | if (response.IsSuccessStatusCode) 40 | { 41 | return response; 42 | } 43 | if ((int)response.StatusCode == 429) 44 | { 45 | 46 | i--; //Explicit ask for retry. Try until successful 47 | if (response.Headers.Contains("Retry-After")) 48 | { 49 | _retryAfter = int.Parse(response.Headers.GetValues("Retry-After").FirstOrDefault()); 50 | Log.Warn($@"Job: {_settings.JobKey}. HttpRetryHandler.Task is being called. 51 | ReqeuestUri: {request.RequestUri} 52 | Response Status Code: {response.StatusCode} 53 | Priority-based throttling in action. 54 | Requested delay (in seconds) between next request: {_retryAfter}"); 55 | } 56 | } 57 | if (_retryAfter > 0 && _settings.RetryCount > 1) 58 | { 59 | Log.Warn($@"Job: {_settings.JobKey}. HttpRetryHandler.Task is being called. 60 | Delaying next request for {_retryAfter} seconds..."); 61 | Thread.Sleep(TimeSpan.FromSeconds(_retryAfter)); 62 | } 63 | } 64 | return response; 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Common/JobSettings/DownloadJobSettings.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using Quartz; 5 | using RecurringIntegrationsScheduler.Common.Contracts; 6 | using RecurringIntegrationsScheduler.Common.Properties; 7 | using System; 8 | using System.Globalization; 9 | using System.IO; 10 | 11 | namespace RecurringIntegrationsScheduler.Common.JobSettings 12 | 13 | { 14 | /// 15 | /// 16 | /// Serialize/deserialize download job settings 17 | /// 18 | /// 19 | public class DownloadJobSettings : Settings 20 | { 21 | /// 22 | /// Initialize and verify settings for job 23 | /// 24 | /// The context. 25 | /// 26 | /// 27 | public override void Initialize(IJobExecutionContext context) 28 | { 29 | var dataMap = context.JobDetail.JobDataMap; 30 | 31 | base.Initialize(context); 32 | 33 | var activityIdStr = dataMap.GetString(SettingsConstants.ActivityId); 34 | if (!Guid.TryParse(activityIdStr, out Guid activityIdGuid) || (Guid.Empty == activityIdGuid)) 35 | { 36 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Activity_Id_of_recurring_job_is_missing_or_is_not_a_GUID_in_job_configuration)); 37 | } 38 | ActivityId = activityIdGuid; 39 | 40 | DownloadSuccessDir = dataMap.GetString(SettingsConstants.DownloadSuccessDir); 41 | if (!string.IsNullOrEmpty(DownloadSuccessDir)) 42 | { 43 | try 44 | { 45 | Directory.CreateDirectory(DownloadSuccessDir); 46 | } 47 | catch (Exception ex) 48 | { 49 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_success_directory_does_not_exist_or_cannot_be_accessed), ex); 50 | } 51 | } 52 | else 53 | { 54 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_success_directory_is_missing_in_job_configuration)); 55 | } 56 | 57 | DownloadErrorsDir = dataMap.GetString(SettingsConstants.DownloadErrorsDir); 58 | if (!string.IsNullOrEmpty(DownloadErrorsDir)) 59 | { 60 | try 61 | { 62 | Directory.CreateDirectory(DownloadErrorsDir); 63 | } 64 | catch (Exception ex) 65 | { 66 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_errors_directory_does_not_exist_or_cannot_be_accessed), ex); 67 | } 68 | } 69 | else 70 | { 71 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_errors_directory_is_missing_in_job_configuration)); 72 | } 73 | 74 | UnzipPackage = dataMap.GetBooleanValue(SettingsConstants.UnzipPackage); 75 | 76 | AddTimestamp = dataMap.GetBooleanValue(SettingsConstants.AddTimestamp); 77 | 78 | DeletePackage = dataMap.GetBooleanValue(SettingsConstants.DeletePackage); 79 | } 80 | 81 | #region Members 82 | 83 | /// 84 | /// Gets or sets the activity identifier. 85 | /// 86 | /// 87 | /// The activity identifier. 88 | /// 89 | public Guid ActivityId { get; set; } 90 | 91 | /// 92 | /// Gets the download success dir. 93 | /// 94 | /// 95 | /// The download success dir. 96 | /// 97 | public string DownloadSuccessDir { get; private set; } 98 | 99 | /// 100 | /// Gets the download errors dir. 101 | /// 102 | /// 103 | /// The download errors dir. 104 | /// 105 | public string DownloadErrorsDir { get; private set; } 106 | 107 | /// 108 | /// Gets a value indicating whether [unzip package]. 109 | /// 110 | /// 111 | /// true if [unzip package]; otherwise, false. 112 | /// 113 | public bool UnzipPackage { get; private set; } 114 | 115 | /// 116 | /// Gets a value indicating whether [add timestamp]. 117 | /// 118 | /// 119 | /// true if [add timestamp]; otherwise, false. 120 | /// 121 | public bool AddTimestamp { get; private set; } 122 | 123 | /// 124 | /// Gets a value indicating whether [delete package]. 125 | /// 126 | /// 127 | /// true if [delete package]; otherwise, false. 128 | /// 129 | public bool DeletePackage { get; private set; } 130 | 131 | #endregion 132 | } 133 | } -------------------------------------------------------------------------------- /Common/JobSettings/ExecutionJobSettings.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using Quartz; 5 | using RecurringIntegrationsScheduler.Common.Contracts; 6 | using RecurringIntegrationsScheduler.Common.Properties; 7 | using System; 8 | using System.Globalization; 9 | using System.IO; 10 | 11 | namespace RecurringIntegrationsScheduler.Common.JobSettings 12 | { 13 | /// 14 | /// Serialize/deserialize execution monitor job settings 15 | /// 16 | /// 17 | public class ExecutionJobSettings : Settings 18 | { 19 | /// 20 | /// Initialize and verify settings for job 21 | /// 22 | /// The context. 23 | /// 24 | /// 25 | public override void Initialize(IJobExecutionContext context) 26 | { 27 | var dataMap = context.JobDetail.JobDataMap; 28 | 29 | base.Initialize(context); 30 | 31 | UploadSuccessDir = dataMap.GetString(SettingsConstants.UploadSuccessDir); 32 | if (!string.IsNullOrEmpty(UploadSuccessDir)) 33 | { 34 | try 35 | { 36 | Directory.CreateDirectory(UploadSuccessDir); 37 | } 38 | catch (Exception ex) 39 | { 40 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Upload_success_directory_does_not_exist_or_cannot_be_accessed), ex); 41 | } 42 | } 43 | else 44 | { 45 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Upload_success_directory_is_missing_in_job_configuration)); 46 | } 47 | 48 | ProcessingSuccessDir = dataMap.GetString(SettingsConstants.ProcessingSuccessDir); 49 | if (!string.IsNullOrEmpty(ProcessingSuccessDir)) 50 | { 51 | try 52 | { 53 | Directory.CreateDirectory(ProcessingSuccessDir); 54 | } 55 | catch (Exception ex) 56 | { 57 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_success_directory_does_not_exist_or_cannot_be_accessed), ex); 58 | } 59 | } 60 | else 61 | { 62 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_success_directory_is_missing_in_job_configuration)); 63 | } 64 | 65 | ProcessingErrorsDir = dataMap.GetString(SettingsConstants.ProcessingErrorsDir); 66 | if (!string.IsNullOrEmpty(ProcessingErrorsDir)) 67 | { 68 | try 69 | { 70 | Directory.CreateDirectory(ProcessingErrorsDir); 71 | } 72 | catch (Exception ex) 73 | { 74 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_errors_directory_does_not_exist_or_cannot_be_accessed), ex); 75 | } 76 | } 77 | else 78 | { 79 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_errors_directory_is_missing_in_job_configuration)); 80 | } 81 | 82 | StatusFileExtension = dataMap.GetString("StatusFileExtension"); 83 | if (string.IsNullOrEmpty(StatusFileExtension)) 84 | { 85 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Extension_of_status_files_is_missing_in_job_configuration)); 86 | } 87 | 88 | GetImportTargetErrorKeysFile = dataMap.GetBooleanValue(SettingsConstants.GetImportTargetErrorKeysFile); 89 | 90 | PackageTemplate = dataMap.GetString(SettingsConstants.PackageTemplate); 91 | 92 | DelayBetweenStatusCheck = dataMap.GetInt(SettingsConstants.DelayBetweenStatusCheck); 93 | } 94 | 95 | #region Members 96 | 97 | /// 98 | /// Gets the upload success dir. 99 | /// 100 | /// 101 | /// The upload success dir. 102 | /// 103 | public string UploadSuccessDir { get; private set; } 104 | 105 | /// 106 | /// Gets the processing success dir. 107 | /// 108 | /// 109 | /// The processing success dir. 110 | /// 111 | public string ProcessingSuccessDir { get; private set; } 112 | 113 | /// 114 | /// Gets the processing errors dir. 115 | /// 116 | /// 117 | /// The processing errors dir. 118 | /// 119 | public string ProcessingErrorsDir { get; private set; } 120 | 121 | /// 122 | /// Gets the status file extension. 123 | /// 124 | /// 125 | /// The status file extension. 126 | /// 127 | public string StatusFileExtension { get; private set; } 128 | 129 | /// 130 | /// Gets a value indicating whether to download [error keys file]. 131 | /// 132 | /// 133 | /// true if [GetImportTargetErrorKeysFile] is set otherwise, false. 134 | /// 135 | public bool GetImportTargetErrorKeysFile { get; private set; } 136 | 137 | /// 138 | /// Package template location. 139 | /// 140 | /// 141 | /// Package template location. 142 | /// 143 | public string PackageTemplate { get; private set; } 144 | 145 | /// 146 | /// Gets or sets delay between status check. 147 | /// 148 | /// 149 | /// Delay between status checks. 150 | /// 151 | public int DelayBetweenStatusCheck { get; private set; } 152 | 153 | #endregion 154 | } 155 | } -------------------------------------------------------------------------------- /Common/JobSettings/ExportJobSettings.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using Quartz; 5 | using RecurringIntegrationsScheduler.Common.Contracts; 6 | using RecurringIntegrationsScheduler.Common.Properties; 7 | using System; 8 | using System.Globalization; 9 | using System.IO; 10 | 11 | namespace RecurringIntegrationsScheduler.Common.JobSettings 12 | { 13 | /// 14 | /// Serialize/deserialize download job settings 15 | /// 16 | /// 17 | public class ExportJobSettings : Settings 18 | { 19 | /// 20 | /// Initialize and verify settings for job 21 | /// 22 | /// The context. 23 | /// 24 | /// 25 | public override void Initialize(IJobExecutionContext context) 26 | { 27 | var dataMap = context.JobDetail.JobDataMap; 28 | 29 | base.Initialize(context); 30 | 31 | DownloadSuccessDir = dataMap.GetString(SettingsConstants.DownloadSuccessDir); 32 | if (!string.IsNullOrEmpty(DownloadSuccessDir)) 33 | { 34 | try 35 | { 36 | Directory.CreateDirectory(DownloadSuccessDir); 37 | } 38 | catch (Exception ex) 39 | { 40 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_success_directory_does_not_exist_or_cannot_be_accessed), ex); 41 | } 42 | } 43 | else 44 | { 45 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_success_directory_is_missing_in_job_configuration)); 46 | } 47 | 48 | DownloadErrorsDir = dataMap.GetString(SettingsConstants.DownloadErrorsDir); 49 | if (!string.IsNullOrEmpty(DownloadErrorsDir)) 50 | { 51 | try 52 | { 53 | Directory.CreateDirectory(DownloadErrorsDir); 54 | } 55 | catch (Exception ex) 56 | { 57 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_errors_directory_does_not_exist_or_cannot_be_accessed), ex); 58 | } 59 | } 60 | else 61 | { 62 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Download_errors_directory_is_missing_in_job_configuration)); 63 | } 64 | 65 | UnzipPackage = dataMap.GetBooleanValue(SettingsConstants.UnzipPackage); 66 | 67 | AddTimestamp = dataMap.GetBooleanValue(SettingsConstants.AddTimestamp); 68 | 69 | DeletePackage = dataMap.GetBooleanValue(SettingsConstants.DeletePackage); 70 | 71 | DataProject = dataMap.GetString(SettingsConstants.DataProject); 72 | 73 | if (string.IsNullOrEmpty(DataProject)) 74 | { 75 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Data_project_is_missing_in_job_configuration)); 76 | } 77 | 78 | Company = dataMap.GetString(SettingsConstants.Company); 79 | 80 | if (string.IsNullOrEmpty(Company)) 81 | { 82 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Company_is_missing_in_job_configuration)); 83 | } 84 | 85 | DelayBetweenStatusCheck = dataMap.GetInt(SettingsConstants.DelayBetweenStatusCheck); 86 | } 87 | 88 | #region Members 89 | 90 | /// 91 | /// Gets the download success dir. 92 | /// 93 | /// 94 | /// The download success dir. 95 | /// 96 | public string DownloadSuccessDir { get; private set; } 97 | 98 | /// 99 | /// Gets the download errors dir. 100 | /// 101 | /// 102 | /// The download errors dir. 103 | /// 104 | public string DownloadErrorsDir { get; private set; } 105 | 106 | /// 107 | /// Gets a value indicating whether [unzip package]. 108 | /// 109 | /// 110 | /// true if [unzip package]; otherwise, false. 111 | /// 112 | public bool UnzipPackage { get; private set; } 113 | 114 | /// 115 | /// Gets a value indicating whether [add timestamp]. 116 | /// 117 | /// 118 | /// true if [add timestamp]; otherwise, false. 119 | /// 120 | public bool AddTimestamp { get; private set; } 121 | 122 | /// 123 | /// Gets a value indicating whether [delete package]. 124 | /// 125 | /// 126 | /// true if [delete package]; otherwise, false. 127 | /// 128 | public bool DeletePackage { get; private set; } 129 | 130 | /// 131 | /// Gets data project. 132 | /// 133 | /// 134 | /// Data project. 135 | /// 136 | public string DataProject { get; private set; } 137 | 138 | /// 139 | /// Gets legal entity id. 140 | /// 141 | /// 142 | /// Legal entity id. 143 | /// 144 | public string Company { get; private set; } 145 | 146 | /// 147 | /// Gets or sets delay between status check. 148 | /// 149 | /// 150 | /// Delay between status checks. 151 | /// 152 | public int DelayBetweenStatusCheck { get; private set; } 153 | 154 | #endregion 155 | } 156 | } -------------------------------------------------------------------------------- /Common/JobSettings/ProcessingJobSettings.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using Quartz; 5 | using RecurringIntegrationsScheduler.Common.Contracts; 6 | using RecurringIntegrationsScheduler.Common.Properties; 7 | using System; 8 | using System.Globalization; 9 | using System.IO; 10 | 11 | namespace RecurringIntegrationsScheduler.Common.JobSettings 12 | { 13 | /// 14 | /// Serialize/deserialize processing job settings 15 | /// 16 | /// 17 | public class ProcessingJobSettings : Settings 18 | { 19 | /// 20 | /// Initialize and verify settings for job 21 | /// 22 | /// The context. 23 | /// 24 | /// 25 | public override void Initialize(IJobExecutionContext context) 26 | { 27 | var dataMap = context.JobDetail.JobDataMap; 28 | 29 | base.Initialize(context); 30 | 31 | var activityIdStr = dataMap.GetString(SettingsConstants.ActivityId); 32 | if (!Guid.TryParse(activityIdStr, out Guid activityIdGuid) || (Guid.Empty == activityIdGuid)) 33 | { 34 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Activity_Id_of_recurring_job_is_missing_or_is_not_a_GUID_in_job_configuration)); 35 | } 36 | 37 | ActivityId = activityIdGuid; 38 | 39 | UploadSuccessDir = dataMap.GetString(SettingsConstants.UploadSuccessDir); 40 | if (!string.IsNullOrEmpty(UploadSuccessDir)) 41 | { 42 | try 43 | { 44 | Directory.CreateDirectory(UploadSuccessDir); 45 | } 46 | catch (Exception ex) 47 | { 48 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Upload_success_directory_does_not_exist_or_cannot_be_accessed), ex); 49 | } 50 | } 51 | else 52 | { 53 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Upload_success_directory_is_missing_in_job_configuration)); 54 | } 55 | 56 | ProcessingSuccessDir = dataMap.GetString(SettingsConstants.ProcessingSuccessDir); 57 | if (!string.IsNullOrEmpty(ProcessingSuccessDir)) 58 | { 59 | try 60 | { 61 | Directory.CreateDirectory(ProcessingSuccessDir); 62 | } 63 | catch (Exception ex) 64 | { 65 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_success_directory_does_not_exist_or_cannot_be_accessed), ex); 66 | } 67 | } 68 | else 69 | { 70 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_success_directory_is_missing_in_job_configuration)); 71 | } 72 | 73 | ProcessingErrorsDir = dataMap.GetString(SettingsConstants.ProcessingErrorsDir); 74 | if (!string.IsNullOrEmpty(ProcessingErrorsDir)) 75 | { 76 | try 77 | { 78 | Directory.CreateDirectory(ProcessingErrorsDir); 79 | } 80 | catch (Exception ex) 81 | { 82 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_errors_directory_does_not_exist_or_cannot_be_accessed), ex); 83 | } 84 | } 85 | else 86 | { 87 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Processing_errors_directory_is_missing_in_job_configuration)); 88 | } 89 | 90 | StatusFileExtension = dataMap.GetString("StatusFileExtension"); 91 | if (string.IsNullOrEmpty(StatusFileExtension)) 92 | { 93 | throw new JobExecutionException(string.Format(CultureInfo.InvariantCulture, Resources.Extension_of_status_files_is_missing_in_job_configuration)); 94 | } 95 | 96 | StatusCheckInterval = dataMap.GetInt(SettingsConstants.DelayBetweenStatusCheck); 97 | } 98 | 99 | #region Members 100 | 101 | /// 102 | /// Gets or sets the activity identifier. 103 | /// 104 | /// 105 | /// The activity identifier. 106 | /// 107 | public Guid ActivityId { get; private set; } 108 | 109 | /// 110 | /// Gets the upload success dir. 111 | /// 112 | /// 113 | /// The upload success dir. 114 | /// 115 | public string UploadSuccessDir { get; private set; } 116 | 117 | /// 118 | /// Gets the processing success dir. 119 | /// 120 | /// 121 | /// The processing success dir. 122 | /// 123 | public string ProcessingSuccessDir { get; private set; } 124 | 125 | /// 126 | /// Gets the processing errors dir. 127 | /// 128 | /// 129 | /// The processing errors dir. 130 | /// 131 | public string ProcessingErrorsDir { get; private set; } 132 | 133 | /// 134 | /// Gets the status file extension. 135 | /// 136 | /// 137 | /// The status file extension. 138 | /// 139 | public string StatusFileExtension { get; private set; } 140 | 141 | /// 142 | /// Gets or sets delay between status check. 143 | /// 144 | /// 145 | /// Delay between status checks. 146 | /// 147 | public int StatusCheckInterval { get; private set; } 148 | 149 | #endregion 150 | } 151 | } -------------------------------------------------------------------------------- /Common/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Common Library")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("96bb041a-3fde-400f-adda-9db19f3274be")] -------------------------------------------------------------------------------- /Common/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Job.Download/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Download Job")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("F4692D5C-4C53-4C30-9992-51F98717861D")] -------------------------------------------------------------------------------- /Job.Download/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Job.Download/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Job.ExecutionMonitor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Execution Monitor Job")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("A46268F5-E858-45F1-9E77-C27D4DA6A626")] -------------------------------------------------------------------------------- /Job.ExecutionMonitor/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Job.ExecutionMonitor/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Job.Export/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Export Job")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("4B0FEF07-196B-47ED-A3DF-49327D9B4A73")] -------------------------------------------------------------------------------- /Job.Export/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Job.Export/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Job.Import/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Import Job")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("0C62174A-DCE9-42A7-8377-2A4C3C8F6E1C")] -------------------------------------------------------------------------------- /Job.Import/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Job.Import/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Job.ProcessingMonitor/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Processing Monitor Job")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("4DC46474-B920-4D13-AFC2-5CF1D9B815AA")] -------------------------------------------------------------------------------- /Job.ProcessingMonitor/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Job.ProcessingMonitor/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Job.Upload/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Upload Job")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("8BC9E0C9-4E2E-4109-BD51-1E79BF548EAA")] -------------------------------------------------------------------------------- /Job.Upload/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Job.Upload/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Recurring Integrations Scheduler 2 | Copyright (c) Microsoft Corporation. All rights reserved. 3 | 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Recurring Integrations Scheduler RIS 2 | 3 | This tool helps to quickly set up and orchestrate file based integration scenarios to and from Dynamics 365 Finance and Dynamics 365 Supply Chain Management. We see this tool to be a good implementation accelerator for use during the implementation phase of the project for data migration, ad hoc file integration needs and to be used as a proof of concept validator among others. 4 | 5 | ## Recurring Integrations Scheduler Service 6 | 7 | Recurring Integrations Scheduler Service is a Windows service that can trigger many integration jobs at predefined schedule. You can schedule jobs and set their recurrence in a very similar way to well-known D365FO batch framework. 8 | 9 | There are six types of integration jobs you can use: [Import](https://github.com/microsoft/Recurring-Integrations-Scheduler/wiki#import-job), [Export](https://github.com/microsoft/Recurring-Integrations-Scheduler/wiki#export-job) and [Execution monitor](https://github.com/microsoft/Recurring-Integrations-Scheduler/wiki#execution-monitor-job) as well as [Upload](https://github.com/microsoft/Recurring-Integrations-Scheduler/wiki#upload-job), [Download](https://github.com/microsoft/Recurring-Integrations-Scheduler/wiki#download-job) and [Processing monitor](https://github.com/microsoft/Recurring-Integrations-Scheduler/wiki#processing-monitor-job). First three use [Data management package REST API](https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/data-management-api). Three other use [Recurring Integrations REST API](https://docs.microsoft.com/en-us/dynamics365/fin-ops-core/dev-itpro/data-entities/recurring-integrations) and recurring data jobs. 10 | More details in wiki. 11 | 12 | Single Recurring Integrations Scheduler service can work with multiple D365FO instances even within different tenants. This enables both production and implementation scenarios where you need to work with multiple non-prod instances. Recurring Integrations Scheduler logs important events to Windows Event Log. It is possible to increase its logging level to log every step for debugging purposes and to trace possible problems. 13 | 14 | ## Recurring Integrations Scheduler App 15 | 16 | Recurring Integrations Scheduler App is a win32 application that can be used as a configuration front-end for Recurring Integrations Scheduler service or as a completely independent, interactive application used to upload or download files to and from Dynamics 365 Finance or SCM without Recurring Integrations Scheduler Service. 17 | It is possible thanks to internal, private scheduler embedded in Recurring Integrations Scheduler App that works exactly the same way as the Scheduler Service does with one difference - it will stop working once the App is closed. 18 | 19 | ## Installation and configuration 20 | 21 | Please check https://github.com/Microsoft/Recurring-Integrations-Scheduler/wiki 22 | 23 | ## Support 24 | 25 | RIS is not officially supported by Microsoft. Please log an issue here on Github if you encounter a bug in RIS. 26 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /Scheduler/Assets/Add_16xMD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Scheduler/Assets/Add_16xMD.png -------------------------------------------------------------------------------- /Scheduler/Assets/Edit_16xMD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Scheduler/Assets/Edit_16xMD.png -------------------------------------------------------------------------------- /Scheduler/Assets/Folder open_32xMD_exp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Scheduler/Assets/Folder open_32xMD_exp.png -------------------------------------------------------------------------------- /Scheduler/Assets/Recurring Integrations Scheduler.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Scheduler/Assets/Recurring Integrations Scheduler.ico -------------------------------------------------------------------------------- /Scheduler/Assets/Remove_16xMD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Scheduler/Assets/Remove_16xMD.png -------------------------------------------------------------------------------- /Scheduler/Assets/ValidateDocument_16x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Scheduler/Assets/ValidateDocument_16x.png -------------------------------------------------------------------------------- /Scheduler/Forms/AadApplicationForm.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using RecurringIntegrationsScheduler.Properties; 5 | using RecurringIntegrationsScheduler.Settings; 6 | using System; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace RecurringIntegrationsScheduler.Forms 11 | { 12 | public partial class AadApplicationForm : Form 13 | { 14 | public AadApplicationForm() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public AadApplication AadApplication { get; set; } 20 | 21 | private void DataJobForm_Load(object sender, EventArgs e) 22 | { 23 | comboBox1.DataSource = Enum.GetValues(typeof(AuthenticationType)); 24 | 25 | if (AadApplication != null) 26 | { 27 | Text = Resources.Edit_Azure_AD_application; 28 | textBox1.Text = AadApplication.Name; 29 | textBox2.Text = AadApplication.ClientId; 30 | textBox3.Text = AadApplication.Secret; 31 | comboBox1.SelectedItem = AadApplication.AuthenticationType; 32 | } 33 | else 34 | { 35 | Text = Resources.Add_Azure_AD_application; 36 | } 37 | } 38 | 39 | private bool InputIsValid() 40 | { 41 | var message = new StringBuilder(); 42 | 43 | if (string.IsNullOrEmpty(textBox1.Text)) 44 | message.AppendLine(Resources.Friendly_name_is_required); 45 | if (string.IsNullOrEmpty(textBox2.Text)) 46 | { 47 | message.AppendLine(Resources.Client_Id_is_required); 48 | } 49 | else 50 | { 51 | var isValid = Guid.TryParse(textBox2.Text, out var guidOutput); 52 | if (!isValid) 53 | message.AppendLine(Resources.Client_Id_is_invalid); 54 | } 55 | if (((AuthenticationType) comboBox1.SelectedItem == AuthenticationType.Service) && 56 | string.IsNullOrEmpty(textBox3.Text)) 57 | message.AppendLine(Resources.Client_secret_is_required); 58 | 59 | if (message.Length > 0) 60 | MessageBox.Show(message.ToString(), Resources.Validation_error, MessageBoxButtons.OK, 61 | MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); 62 | return message.Length == 0; 63 | } 64 | 65 | private void ButtonOK_Click(object sender, EventArgs e) 66 | { 67 | if (InputIsValid()) 68 | AadApplication = new AadApplication 69 | { 70 | Name = textBox1.Text, 71 | ClientId = textBox2.Text, 72 | Secret = textBox3.Text, 73 | AuthenticationType = (AuthenticationType) comboBox1.SelectedItem 74 | }; 75 | else 76 | DialogResult = DialogResult.None; 77 | } 78 | 79 | private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e) 80 | { 81 | textBox3.Enabled = (AuthenticationType) comboBox1.SelectedItem == AuthenticationType.Service; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /Scheduler/Forms/AadApplicationForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Scheduler/Forms/AboutBox.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using RecurringIntegrationsScheduler.Properties; 5 | using System.IO; 6 | using System.Reflection; 7 | using System.Windows.Forms; 8 | 9 | namespace RecurringIntegrationsScheduler.Forms 10 | { 11 | internal sealed partial class AboutBox : Form 12 | { 13 | public AboutBox() 14 | { 15 | InitializeComponent(); 16 | Text = string.Format(Resources.About_0, AssemblyTitle); 17 | labelProductName.Text = AssemblyProduct; 18 | labelVersion.Text = string.Format(Resources.Version_0, AssemblyVersion); 19 | } 20 | 21 | #region Assembly Attribute Accessors 22 | 23 | private static string AssemblyTitle 24 | { 25 | get 26 | { 27 | var attributes = Assembly.GetExecutingAssembly() 28 | .GetCustomAttributes(typeof(AssemblyTitleAttribute), false); 29 | if (attributes.Length > 0) 30 | { 31 | var titleAttribute = (AssemblyTitleAttribute) attributes[0]; 32 | if (titleAttribute.Title != "") 33 | return titleAttribute.Title; 34 | } 35 | return Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().CodeBase); 36 | } 37 | } 38 | 39 | private static string AssemblyVersion => Assembly.GetExecutingAssembly().GetName().Version.ToString(); 40 | 41 | private static string AssemblyProduct 42 | { 43 | get 44 | { 45 | var attributes = Assembly.GetExecutingAssembly() 46 | .GetCustomAttributes(typeof(AssemblyProductAttribute), false); 47 | return attributes.Length == 0 ? "" : ((AssemblyProductAttribute) attributes[0]).Product; 48 | } 49 | } 50 | 51 | #endregion 52 | } 53 | } -------------------------------------------------------------------------------- /Scheduler/Forms/Connect.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System; 5 | using System.Windows.Forms; 6 | 7 | namespace RecurringIntegrationsScheduler.Forms 8 | { 9 | public partial class Connect : Form 10 | { 11 | public Connect() 12 | { 13 | InitializeComponent(); 14 | } 15 | 16 | public string Server { get; private set; } 17 | public int Port { get; private set; } 18 | public string Scheduler { get; private set; } 19 | public bool Cancelled { get; private set; } 20 | 21 | private void CancelButton_Click(object sender, EventArgs e) 22 | { 23 | Close(); 24 | } 25 | 26 | private void ConnectButton_Click(object sender, EventArgs e) 27 | { 28 | Cancelled = false; 29 | Server = serverTextBox.Text; 30 | Port = int.Parse(portTextBox.Text); 31 | Scheduler = schedulerTextBox.Text; 32 | Close(); 33 | } 34 | 35 | private void ServerConnectForm_Load(object sender, EventArgs e) 36 | { 37 | Cancelled = true; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /Scheduler/Forms/Connect.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Scheduler/Forms/CronExamples.Designer.cs: -------------------------------------------------------------------------------- 1 | using RecurringIntegrationsScheduler.Properties; 2 | 3 | namespace RecurringIntegrationsScheduler.Forms 4 | { 5 | partial class CronExamples 6 | { 7 | /// 8 | /// Required designer variable. 9 | /// 10 | private System.ComponentModel.IContainer components = null; 11 | 12 | /// 13 | /// Clean up any resources being used. 14 | /// 15 | /// true if managed resources should be disposed; otherwise, false. 16 | protected override void Dispose(bool disposing) 17 | { 18 | if (disposing && (components != null)) 19 | { 20 | components.Dispose(); 21 | } 22 | base.Dispose(disposing); 23 | } 24 | 25 | #region Windows Form Designer generated code 26 | 27 | /// 28 | /// Required method for Designer support - do not modify 29 | /// the contents of this method with the code editor. 30 | /// 31 | private void InitializeComponent() 32 | { 33 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(CronExamples)); 34 | this.textBox1 = new System.Windows.Forms.TextBox(); 35 | this.SuspendLayout(); 36 | // 37 | // textBox1 38 | // 39 | this.textBox1.Dock = System.Windows.Forms.DockStyle.Fill; 40 | this.textBox1.Font = new System.Drawing.Font("Courier New", 8.142858F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 41 | this.textBox1.Location = new System.Drawing.Point(0, 0); 42 | this.textBox1.Margin = new System.Windows.Forms.Padding(7, 6, 7, 6); 43 | this.textBox1.Multiline = true; 44 | this.textBox1.Name = "textBox1"; 45 | this.textBox1.ReadOnly = true; 46 | this.textBox1.Size = new System.Drawing.Size(1544, 541); 47 | this.textBox1.TabIndex = 0; 48 | this.textBox1.Text = resources.GetString("textBox1.Text"); 49 | // 50 | // CronExamples 51 | // 52 | this.AutoScaleDimensions = new System.Drawing.SizeF(12F, 25F); 53 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 54 | this.AutoScroll = true; 55 | this.ClientSize = new System.Drawing.Size(1544, 541); 56 | this.Controls.Add(this.textBox1); 57 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 58 | this.Margin = new System.Windows.Forms.Padding(7, 6, 7, 6); 59 | this.MaximizeBox = false; 60 | this.MaximumSize = new System.Drawing.Size(1570, 612); 61 | this.MinimizeBox = false; 62 | this.MinimumSize = new System.Drawing.Size(1570, 612); 63 | this.Name = "CronExamples"; 64 | this.ShowIcon = false; 65 | this.ShowInTaskbar = false; 66 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 67 | this.Text = "Cron examples"; 68 | this.Load += new System.EventHandler(this.CronExamples_Load); 69 | this.ResumeLayout(false); 70 | this.PerformLayout(); 71 | 72 | } 73 | 74 | #endregion 75 | 76 | private System.Windows.Forms.TextBox textBox1; 77 | } 78 | } -------------------------------------------------------------------------------- /Scheduler/Forms/CronExamples.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System; 5 | using System.Windows.Forms; 6 | 7 | namespace RecurringIntegrationsScheduler.Forms 8 | { 9 | public partial class CronExamples : Form 10 | { 11 | public CronExamples() 12 | { 13 | InitializeComponent(); 14 | } 15 | 16 | private void CronExamples_Load(object sender, EventArgs e) 17 | { 18 | textBox1.SelectionStart = 0; 19 | textBox1.SelectionLength = 0; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Scheduler/Forms/DataJobForm.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using RecurringIntegrationsScheduler.Properties; 5 | using RecurringIntegrationsScheduler.Settings; 6 | using System; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace RecurringIntegrationsScheduler.Forms 11 | { 12 | public partial class DataJobForm : Form 13 | { 14 | public DataJobForm() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public DataJob DataJob { get; set; } 20 | 21 | private void DataJobForm_Load(object sender, EventArgs e) 22 | { 23 | comboBox1.DataSource = Enum.GetValues(typeof(DataJobType)); 24 | 25 | if (DataJob != null) 26 | { 27 | Text = Resources.Edit_data_job; 28 | textBox1.Text = DataJob.Name; 29 | textBox2.Text = DataJob.ActivityId; 30 | textBox3.Text = DataJob.EntityName; 31 | comboBox1.SelectedItem = DataJob.Type; 32 | } 33 | else 34 | { 35 | Text = Resources.Add_data_job; 36 | } 37 | } 38 | 39 | private bool InputIsValid() 40 | { 41 | var message = new StringBuilder(); 42 | 43 | if (string.IsNullOrEmpty(textBox1.Text)) 44 | message.AppendLine(Resources.Friendly_name_is_required); 45 | if (string.IsNullOrEmpty(textBox2.Text)) 46 | { 47 | message.AppendLine(Resources.Activity_Id_is_required); 48 | } 49 | else 50 | { 51 | var isValid = Guid.TryParse(textBox2.Text, out var guidOutput); 52 | if (!isValid) 53 | message.AppendLine(Resources.Activity_Id_is_invalid); 54 | } 55 | 56 | if (message.Length > 0) 57 | MessageBox.Show(message.ToString(), Resources.Validation_error, MessageBoxButtons.OK, 58 | MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); 59 | return message.Length == 0; 60 | } 61 | 62 | private void ButtonOK_Click(object sender, EventArgs e) 63 | { 64 | if (InputIsValid()) 65 | DataJob = new DataJob 66 | { 67 | Name = textBox1.Text, 68 | ActivityId = textBox2.Text, 69 | EntityName = textBox3.Text, 70 | Type = (DataJobType) comboBox1.SelectedItem 71 | }; 72 | else 73 | DialogResult = DialogResult.None; 74 | } 75 | 76 | private void ComboBox1_SelectedIndexChanged(object sender, EventArgs e) 77 | { 78 | textBox3.Enabled = (DataJobType) comboBox1.SelectedItem == DataJobType.Upload; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /Scheduler/Forms/DataJobForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Scheduler/Forms/InstanceForm.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using RecurringIntegrationsScheduler.Properties; 5 | using RecurringIntegrationsScheduler.Settings; 6 | using System; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace RecurringIntegrationsScheduler.Forms 11 | { 12 | public partial class InstanceForm : Form 13 | { 14 | public InstanceForm() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public Instance Instance { get; set; } 20 | 21 | private void InstanceForm_Load(object sender, EventArgs e) 22 | { 23 | if (Instance != null) 24 | { 25 | Text = Resources.Edit_instance; 26 | textBox1.Text = Instance.Name; 27 | textBox2.Text = Instance.AosUri; 28 | textBox3.Text = Instance.AzureAuthEndpoint; 29 | textBox4.Text = Instance.AadTenant; 30 | checkBox1.Checked = Instance.UseADAL; 31 | } 32 | else 33 | { 34 | Text = Resources.Add_instance; 35 | textBox3.Text = @"https://login.microsoftonline.com"; 36 | } 37 | } 38 | 39 | private bool InputIsValid() 40 | { 41 | var message = new StringBuilder(); 42 | 43 | if (string.IsNullOrEmpty(textBox1.Text)) 44 | message.AppendLine(Resources.Friendly_name_is_required); 45 | if (string.IsNullOrEmpty(textBox2.Text)) 46 | message.AppendLine(Resources.AOS_URL_is_required); 47 | else if (!CheckUrl(textBox2.Text)) 48 | message.AppendLine(Resources.AOS_URL_is_invalid); 49 | if (string.IsNullOrEmpty(textBox3.Text)) 50 | message.AppendLine(Resources.Authentication_endpoint_is_required); 51 | else if (!CheckUrl(textBox3.Text)) 52 | message.AppendLine(Resources.Authentication_endpoint_URL_is_invalid); 53 | if (string.IsNullOrEmpty(textBox4.Text)) 54 | message.AppendLine(Resources.Tenant_Id_is_required); 55 | 56 | if (message.Length > 0) 57 | MessageBox.Show(message.ToString(), Resources.Validation_error, MessageBoxButtons.OK, 58 | MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); 59 | return message.Length == 0; 60 | } 61 | 62 | private static bool CheckUrl(string url) 63 | { 64 | var result = Uri.TryCreate(url, UriKind.Absolute, out Uri uriResult) 65 | && ((uriResult.Scheme == Uri.UriSchemeHttp) || (uriResult.Scheme == Uri.UriSchemeHttps)); 66 | return result; 67 | } 68 | 69 | private void ButtonOK_Click(object sender, EventArgs e) 70 | { 71 | if (InputIsValid()) 72 | Instance = new Instance 73 | { 74 | Name = textBox1.Text, 75 | AosUri = textBox2.Text.TrimEnd('/'), 76 | AzureAuthEndpoint = textBox3.Text.TrimEnd('/'), 77 | AadTenant = textBox4.Text, 78 | UseADAL = checkBox1.Checked 79 | }; 80 | else 81 | DialogResult = DialogResult.None; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /Scheduler/Forms/InstanceForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Scheduler/Forms/JobGroupForm.Designer.cs: -------------------------------------------------------------------------------- 1 | using RecurringIntegrationsScheduler.Properties; 2 | 3 | namespace RecurringIntegrationsScheduler.Forms 4 | { 5 | partial class JobGroupForm 6 | { 7 | /// 8 | /// Required designer variable. 9 | /// 10 | private System.ComponentModel.IContainer components = null; 11 | 12 | /// 13 | /// Clean up any resources being used. 14 | /// 15 | /// true if managed resources should be disposed; otherwise, false. 16 | protected override void Dispose(bool disposing) 17 | { 18 | if (disposing && (components != null)) 19 | { 20 | components.Dispose(); 21 | } 22 | base.Dispose(disposing); 23 | } 24 | 25 | #region Windows Form Designer generated code 26 | 27 | /// 28 | /// Required method for Designer support - do not modify 29 | /// the contents of this method with the code editor. 30 | /// 31 | private void InitializeComponent() 32 | { 33 | this.panel1 = new System.Windows.Forms.Panel(); 34 | this.ButtonCancel = new System.Windows.Forms.Button(); 35 | this.ButtonOK = new System.Windows.Forms.Button(); 36 | this.label1 = new System.Windows.Forms.Label(); 37 | this.textBox1 = new System.Windows.Forms.TextBox(); 38 | this.panel1.SuspendLayout(); 39 | this.SuspendLayout(); 40 | // 41 | // panel1 42 | // 43 | this.panel1.Controls.Add(this.ButtonCancel); 44 | this.panel1.Controls.Add(this.ButtonOK); 45 | this.panel1.Controls.Add(this.label1); 46 | this.panel1.Controls.Add(this.textBox1); 47 | this.panel1.Location = new System.Drawing.Point(18, 18); 48 | this.panel1.Margin = new System.Windows.Forms.Padding(4); 49 | this.panel1.Name = "panel1"; 50 | this.panel1.Size = new System.Drawing.Size(538, 150); 51 | this.panel1.TabIndex = 0; 52 | // 53 | // ButtonCancel 54 | // 55 | this.ButtonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 56 | this.ButtonCancel.Location = new System.Drawing.Point(267, 86); 57 | this.ButtonCancel.Margin = new System.Windows.Forms.Padding(4); 58 | this.ButtonCancel.Name = "ButtonCancel"; 59 | this.ButtonCancel.Size = new System.Drawing.Size(125, 52); 60 | this.ButtonCancel.TabIndex = 9; 61 | this.ButtonCancel.Text = global::RecurringIntegrationsScheduler.Properties.Resources.Cancel; 62 | this.ButtonCancel.UseVisualStyleBackColor = true; 63 | // 64 | // ButtonOK 65 | // 66 | this.ButtonOK.DialogResult = System.Windows.Forms.DialogResult.OK; 67 | this.ButtonOK.Location = new System.Drawing.Point(133, 86); 68 | this.ButtonOK.Margin = new System.Windows.Forms.Padding(4); 69 | this.ButtonOK.Name = "ButtonOK"; 70 | this.ButtonOK.Size = new System.Drawing.Size(125, 52); 71 | this.ButtonOK.TabIndex = 8; 72 | this.ButtonOK.Text = global::RecurringIntegrationsScheduler.Properties.Resources.OK; 73 | this.ButtonOK.UseVisualStyleBackColor = true; 74 | this.ButtonOK.Click += new System.EventHandler(this.ButtonOK_Click); 75 | // 76 | // label1 77 | // 78 | this.label1.AutoSize = true; 79 | this.label1.Location = new System.Drawing.Point(22, 32); 80 | this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); 81 | this.label1.Name = "label1"; 82 | this.label1.Size = new System.Drawing.Size(184, 25); 83 | this.label1.TabIndex = 1; 84 | this.label1.Text = "RIS job group name"; 85 | // 86 | // textBox1 87 | // 88 | this.textBox1.Location = new System.Drawing.Point(214, 27); 89 | this.textBox1.Margin = new System.Windows.Forms.Padding(4); 90 | this.textBox1.Name = "textBox1"; 91 | this.textBox1.Size = new System.Drawing.Size(296, 29); 92 | this.textBox1.TabIndex = 0; 93 | // 94 | // JobGroupForm 95 | // 96 | this.AcceptButton = this.ButtonOK; 97 | this.AutoScaleDimensions = new System.Drawing.SizeF(11F, 24F); 98 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 99 | this.CancelButton = this.ButtonCancel; 100 | this.ClientSize = new System.Drawing.Size(568, 165); 101 | this.Controls.Add(this.panel1); 102 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 103 | this.Margin = new System.Windows.Forms.Padding(4); 104 | this.MaximizeBox = false; 105 | this.MaximumSize = new System.Drawing.Size(592, 229); 106 | this.MinimizeBox = false; 107 | this.Name = "JobGroupForm"; 108 | this.ShowIcon = false; 109 | this.ShowInTaskbar = false; 110 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; 111 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 112 | this.Load += new System.EventHandler(this.JobGroupForm_Load); 113 | this.panel1.ResumeLayout(false); 114 | this.panel1.PerformLayout(); 115 | this.ResumeLayout(false); 116 | 117 | } 118 | 119 | #endregion 120 | 121 | private System.Windows.Forms.Panel panel1; 122 | private System.Windows.Forms.Button ButtonCancel; 123 | private System.Windows.Forms.Button ButtonOK; 124 | private System.Windows.Forms.Label label1; 125 | private System.Windows.Forms.TextBox textBox1; 126 | } 127 | } -------------------------------------------------------------------------------- /Scheduler/Forms/JobGroupForm.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using RecurringIntegrationsScheduler.Properties; 5 | using RecurringIntegrationsScheduler.Settings; 6 | using System; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace RecurringIntegrationsScheduler.Forms 11 | { 12 | public partial class JobGroupForm : Form 13 | { 14 | public JobGroupForm() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public JobGroup JobGroup { get; set; } 20 | 21 | private void JobGroupForm_Load(object sender, EventArgs e) 22 | { 23 | if (JobGroup != null) 24 | { 25 | Text = Resources.Edit_job_group; 26 | textBox1.Text = JobGroup.Name; 27 | } 28 | else 29 | { 30 | Text = Resources.Add_job_group; 31 | } 32 | } 33 | 34 | private bool InputIsValid() 35 | { 36 | var message = new StringBuilder(); 37 | 38 | if (string.IsNullOrEmpty(textBox1.Text)) 39 | message.AppendLine(Resources.Job_group_name_is_required); 40 | 41 | if (message.Length > 0) 42 | MessageBox.Show(message.ToString(), Resources.Validation_error, MessageBoxButtons.OK, 43 | MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); 44 | return message.Length == 0; 45 | } 46 | 47 | private void ButtonOK_Click(object sender, EventArgs e) 48 | { 49 | if (InputIsValid()) 50 | JobGroup = new JobGroup 51 | { 52 | Name = textBox1.Text 53 | }; 54 | else 55 | DialogResult = DialogResult.None; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /Scheduler/Forms/JobGroupForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Scheduler/Forms/UserForm.Designer.cs: -------------------------------------------------------------------------------- 1 | using RecurringIntegrationsScheduler.Properties; 2 | 3 | namespace RecurringIntegrationsScheduler.Forms 4 | { 5 | partial class UserForm 6 | { 7 | /// 8 | /// Required designer variable. 9 | /// 10 | private System.ComponentModel.IContainer components = null; 11 | 12 | /// 13 | /// Clean up any resources being used. 14 | /// 15 | /// true if managed resources should be disposed; otherwise, false. 16 | protected override void Dispose(bool disposing) 17 | { 18 | if (disposing && (components != null)) 19 | { 20 | components.Dispose(); 21 | } 22 | base.Dispose(disposing); 23 | } 24 | 25 | #region Windows Form Designer generated code 26 | 27 | /// 28 | /// Required method for Designer support - do not modify 29 | /// the contents of this method with the code editor. 30 | /// 31 | private void InitializeComponent() 32 | { 33 | this.panel1 = new System.Windows.Forms.Panel(); 34 | this.ButtonCancel = new System.Windows.Forms.Button(); 35 | this.ButtonOK = new System.Windows.Forms.Button(); 36 | this.label2 = new System.Windows.Forms.Label(); 37 | this.textBox2 = new System.Windows.Forms.TextBox(); 38 | this.label1 = new System.Windows.Forms.Label(); 39 | this.textBox1 = new System.Windows.Forms.TextBox(); 40 | this.panel1.SuspendLayout(); 41 | this.SuspendLayout(); 42 | // 43 | // panel1 44 | // 45 | this.panel1.Controls.Add(this.ButtonCancel); 46 | this.panel1.Controls.Add(this.ButtonOK); 47 | this.panel1.Controls.Add(this.label2); 48 | this.panel1.Controls.Add(this.textBox2); 49 | this.panel1.Controls.Add(this.label1); 50 | this.panel1.Controls.Add(this.textBox1); 51 | this.panel1.Location = new System.Drawing.Point(13, 12); 52 | this.panel1.Name = "panel1"; 53 | this.panel1.Size = new System.Drawing.Size(360, 112); 54 | this.panel1.TabIndex = 0; 55 | // 56 | // ButtonCancel 57 | // 58 | this.ButtonCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 59 | this.ButtonCancel.Location = new System.Drawing.Point(186, 74); 60 | this.ButtonCancel.Name = "ButtonCancel"; 61 | this.ButtonCancel.Size = new System.Drawing.Size(91, 35); 62 | this.ButtonCancel.TabIndex = 9; 63 | this.ButtonCancel.Text = Resources.Cancel; 64 | this.ButtonCancel.UseVisualStyleBackColor = true; 65 | // 66 | // ButtonOK 67 | // 68 | this.ButtonOK.DialogResult = System.Windows.Forms.DialogResult.OK; 69 | this.ButtonOK.Location = new System.Drawing.Point(89, 74); 70 | this.ButtonOK.Name = "ButtonOK"; 71 | this.ButtonOK.Size = new System.Drawing.Size(91, 35); 72 | this.ButtonOK.TabIndex = 8; 73 | this.ButtonOK.Text = Resources.OK; 74 | this.ButtonOK.UseVisualStyleBackColor = true; 75 | this.ButtonOK.Click += new System.EventHandler(this.ButtonOK_Click); 76 | // 77 | // label2 78 | // 79 | this.label2.AutoSize = true; 80 | this.label2.Location = new System.Drawing.Point(44, 49); 81 | this.label2.Name = "label2"; 82 | this.label2.Size = new System.Drawing.Size(69, 17); 83 | this.label2.TabIndex = 3; 84 | this.label2.Text = Resources.Password; 85 | // 86 | // textBox2 87 | // 88 | this.textBox2.Location = new System.Drawing.Point(119, 46); 89 | this.textBox2.Name = "textBox2"; 90 | this.textBox2.PasswordChar = '●'; 91 | this.textBox2.Size = new System.Drawing.Size(209, 22); 92 | this.textBox2.TabIndex = 2; 93 | // 94 | // label1 95 | // 96 | this.label1.AutoSize = true; 97 | this.label1.Location = new System.Drawing.Point(27, 21); 98 | this.label1.Name = "label1"; 99 | this.label1.Size = new System.Drawing.Size(86, 17); 100 | this.label1.TabIndex = 1; 101 | this.label1.Text = Resources.Login_UPN; 102 | // 103 | // textBox1 104 | // 105 | this.textBox1.Location = new System.Drawing.Point(119, 18); 106 | this.textBox1.Name = "textBox1"; 107 | this.textBox1.Size = new System.Drawing.Size(209, 22); 108 | this.textBox1.TabIndex = 0; 109 | // 110 | // UserForm 111 | // 112 | this.AcceptButton = this.ButtonOK; 113 | this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F); 114 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 115 | this.CancelButton = this.ButtonCancel; 116 | this.ClientSize = new System.Drawing.Size(380, 132); 117 | this.Controls.Add(this.panel1); 118 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 119 | this.MaximizeBox = false; 120 | this.MaximumSize = new System.Drawing.Size(398, 179); 121 | this.MinimizeBox = false; 122 | this.Name = "UserForm"; 123 | this.ShowIcon = false; 124 | this.ShowInTaskbar = false; 125 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; 126 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; 127 | this.Load += new System.EventHandler(this.UserForm_Load); 128 | this.panel1.ResumeLayout(false); 129 | this.panel1.PerformLayout(); 130 | this.ResumeLayout(false); 131 | 132 | } 133 | 134 | #endregion 135 | 136 | private System.Windows.Forms.Panel panel1; 137 | private System.Windows.Forms.Button ButtonCancel; 138 | private System.Windows.Forms.Button ButtonOK; 139 | private System.Windows.Forms.Label label2; 140 | private System.Windows.Forms.TextBox textBox2; 141 | private System.Windows.Forms.Label label1; 142 | private System.Windows.Forms.TextBox textBox1; 143 | } 144 | } -------------------------------------------------------------------------------- /Scheduler/Forms/UserForm.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using RecurringIntegrationsScheduler.Properties; 5 | using RecurringIntegrationsScheduler.Settings; 6 | using System; 7 | using System.Text; 8 | using System.Windows.Forms; 9 | 10 | namespace RecurringIntegrationsScheduler.Forms 11 | { 12 | public partial class UserForm : Form 13 | { 14 | public UserForm() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | public User User { get; set; } 20 | 21 | private void UserForm_Load(object sender, EventArgs e) 22 | { 23 | if (User != null) 24 | { 25 | Text = Resources.Edit_user; 26 | textBox1.Text = User.Login; 27 | textBox2.Text = User.Password; 28 | } 29 | else 30 | { 31 | Text = Resources.Add_user; 32 | } 33 | } 34 | 35 | private bool InputIsValid() 36 | { 37 | var message = new StringBuilder(); 38 | 39 | if (string.IsNullOrEmpty(textBox1.Text)) 40 | message.AppendLine(Resources.User_login_is_required); 41 | if (string.IsNullOrEmpty(textBox2.Text)) 42 | message.AppendLine(Resources.User_password_is_required); 43 | 44 | if (message.Length > 0) 45 | MessageBox.Show(message.ToString(), Resources.Validation_error, MessageBoxButtons.OK, 46 | MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1); 47 | return message.Length == 0; 48 | } 49 | 50 | private void ButtonOK_Click(object sender, EventArgs e) 51 | { 52 | if (InputIsValid()) 53 | User = new User 54 | { 55 | Login = textBox1.Text, 56 | Password = textBox2.Text 57 | }; 58 | else 59 | DialogResult = DialogResult.None; 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /Scheduler/Forms/UserForm.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Scheduler/Forms/ValidateConnection.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /Scheduler/FormsHelper.cs: -------------------------------------------------------------------------------- 1 | using Quartz; 2 | using RecurringIntegrationsScheduler.Properties; 3 | using System; 4 | using System.Linq; 5 | using System.Windows.Forms; 6 | 7 | namespace RecurringIntegrationsScheduler 8 | { 9 | internal class FormsHelper 10 | { 11 | public static int DropDownWidth(ComboBox myCombo) 12 | { 13 | int minWidth = 1; 14 | foreach (var item in myCombo.Items) 15 | { 16 | int temp = TextRenderer.MeasureText(item.ToString(), myCombo.Font).Width; 17 | if (temp > minWidth) 18 | { 19 | minWidth = temp; 20 | } 21 | } 22 | return minWidth; 23 | } 24 | 25 | public static DateTimeOffset? GetScheduleForCron(string cronexpression, DateTimeOffset date) 26 | { 27 | try 28 | { 29 | var cron = new CronExpression(cronexpression); 30 | return cron.GetNextValidTimeAfter(date); 31 | } 32 | catch (Exception ex) 33 | { 34 | MessageBox.Show(ex.Message, Resources.Cron_expression_is_invalid); 35 | return DateTimeOffset.MinValue; 36 | } 37 | } 38 | 39 | public static void SetDropDownsWidth(Control parentCtrl) 40 | { 41 | parentCtrl.Controls 42 | .OfType() 43 | .ToList() 44 | .ForEach(t => t.DropDownWidth = DropDownWidth(t)); 45 | 46 | foreach (Control c in parentCtrl.Controls) 47 | { 48 | SetDropDownsWidth(c); 49 | } 50 | } 51 | 52 | public static void TrimTextBoxes(Control parentCtrl) 53 | { 54 | parentCtrl.Controls //Trim all textboxes 55 | .OfType() 56 | .ToList() 57 | .ForEach(t => t.Text = t.Text.Trim()); 58 | 59 | foreach (Control c in parentCtrl.Controls) 60 | { 61 | TrimTextBoxes(c); 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Scheduler/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "", Scope = "member", Target = "~M:RecurringIntegrationsScheduler.Forms.UploadJobV3.GetScheduleForCron(System.String,System.DateTimeOffset)~System.Nullable{System.DateTimeOffset}")] 7 | -------------------------------------------------------------------------------- /Scheduler/Program.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using Bluegrams.Application; 5 | using RecurringIntegrationsScheduler.Forms; 6 | using System; 7 | using System.Windows.Forms; 8 | 9 | namespace RecurringIntegrationsScheduler 10 | { 11 | internal static class Program 12 | { 13 | /// 14 | /// The main entry point for the application. 15 | /// 16 | [STAThread] 17 | private static void Main() 18 | { 19 | PortableSettingsProvider.ApplyProvider(Properties.Settings.Default); 20 | Application.EnableVisualStyles(); 21 | Application.SetCompatibleTextRenderingDefault(false); 22 | Application.Run(new MainForm()); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Scheduler/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("Microsoft")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("4F945CF2-CF0C-4A90-AB34-2354A260B4AF")] -------------------------------------------------------------------------------- /Scheduler/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | UploadErrors 7 | 8 | 9 | UploadSuccess 10 | 11 | 12 | ProcessingSuccess 13 | 14 | 15 | Errors 16 | 17 | 18 | Input 19 | 20 | 21 | ProcessingErrors 22 | 23 | 24 | True 25 | 26 | 27 | <?xml version="1.0" encoding="utf-16"?> 28 | <ArrayOfDataJob xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 29 | <DataJob> 30 | <Name>Customers import</Name> 31 | <ActivityId>00000000-0000-0000-0000-000000000000</ActivityId> 32 | <EntityName>Customers</EntityName> 33 | <Type>Download</Type> 34 | </DataJob> 35 | </ArrayOfDataJob> 36 | 37 | 38 | <?xml version="1.0" encoding="utf-16"?> 39 | <ArrayOfInstance xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 40 | <Instance> 41 | <Name>Contoso PROD</Name> 42 | <AosUri>https://contoso.production.operations.dynamics.com</AosUri> 43 | <AzureAuthEndpoint>https://login.microsoftonline.com</AzureAuthEndpoint> 44 | <AadTenant>contoso.com</AadTenant> 45 | </Instance> 46 | </ArrayOfInstance> 47 | 48 | 49 | <?xml version="1.0" encoding="utf-16"?> 50 | <ArrayOfUser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 51 | <User> 52 | <Login>admin@contoso.com</Login> 53 | <Password>pass@word1</Password> 54 | </User> 55 | </ArrayOfUser> 56 | 57 | 58 | <?xml version="1.0" encoding="utf-16"?> 59 | <ArrayOfJobGroup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 60 | <JobGroup> 61 | <Name>Import jobs</Name> 62 | </JobGroup> 63 | <JobGroup> 64 | <Name>Export jobs</Name> 65 | </JobGroup> 66 | <JobGroup> 67 | <Name>Download jobs</Name> 68 | </JobGroup> 69 | <JobGroup> 70 | <Name>Upload jobs</Name> 71 | </JobGroup> 72 | </ArrayOfJobGroup> 73 | 74 | 75 | <?xml version="1.0" encoding="utf-16"?> 76 | <ArrayOfAadApplication xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 77 | <AadApplication> 78 | <Name>User impersonation</Name> 79 | <ClientId>00000000-0000-0000-0000-000000000000</ClientId> 80 | <Secret /> 81 | <AuthenticationType>User</AuthenticationType> 82 | </AadApplication> 83 | <AadApplication> 84 | <Name>Service to Service</Name> 85 | <ClientId>00000000-0000-0000-0000-000000000000</ClientId> 86 | <Secret /> 87 | <AuthenticationType>Service</AuthenticationType> 88 | </AadApplication> 89 | </ArrayOfAadApplication> 90 | 91 | 92 | -------------------------------------------------------------------------------- /Scheduler/Settings/Settings.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System; 5 | using System.ComponentModel; 6 | 7 | namespace RecurringIntegrationsScheduler.Settings 8 | { 9 | [Serializable] 10 | public class AadApplication 11 | { 12 | /// 13 | /// Gets or sets the name. 14 | /// 15 | /// 16 | /// The name. 17 | /// 18 | public string Name { get; set; } 19 | 20 | /// 21 | /// Gets or sets the client identifier. 22 | /// 23 | /// 24 | /// The client identifier. 25 | /// 26 | public string ClientId { get; set; } 27 | 28 | /// 29 | /// Gets or sets the secret. 30 | /// 31 | /// 32 | /// The secret. 33 | /// 34 | public string Secret { get; set; } 35 | 36 | /// 37 | /// Gets or sets the type. 38 | /// 39 | /// 40 | /// The type. 41 | /// 42 | public AuthenticationType AuthenticationType { get; set; } 43 | } 44 | 45 | /// 46 | /// 47 | /// 48 | public enum AuthenticationType 49 | { 50 | User, 51 | Service 52 | } 53 | 54 | /// 55 | /// 56 | /// 57 | /// 58 | public class AadApplications : BindingList 59 | { 60 | } 61 | 62 | /// 63 | /// 64 | /// 65 | [Serializable] 66 | public class DataJob 67 | { 68 | /// 69 | /// Gets or sets the name. 70 | /// 71 | /// 72 | /// The name. 73 | /// 74 | public string Name { get; set; } 75 | 76 | /// 77 | /// Gets or sets the activity identifier. 78 | /// 79 | /// 80 | /// The activity identifier. 81 | /// 82 | public string ActivityId { get; set; } 83 | 84 | /// 85 | /// Gets or sets the name of the entity. 86 | /// 87 | /// 88 | /// The name of the entity. 89 | /// 90 | public string EntityName { get; set; } 91 | 92 | /// 93 | /// Gets or sets the type. 94 | /// 95 | /// 96 | /// The type. 97 | /// 98 | public DataJobType Type { get; set; } 99 | } 100 | 101 | /// 102 | /// 103 | /// 104 | public enum DataJobType 105 | { 106 | Download, 107 | Upload 108 | } 109 | 110 | /// 111 | /// 112 | /// 113 | /// 114 | public class DataJobs : BindingList 115 | { 116 | } 117 | 118 | /// 119 | /// 120 | /// 121 | [Serializable] 122 | public class Instance 123 | { 124 | /// 125 | /// Gets or sets the name. 126 | /// 127 | /// 128 | /// The name. 129 | /// 130 | public string Name { get; set; } 131 | 132 | /// 133 | /// Gets or sets the aos URI. 134 | /// 135 | /// 136 | /// The aos URI. 137 | /// 138 | public string AosUri { get; set; } 139 | 140 | /// 141 | /// Gets or sets the azure authentication endpoint. 142 | /// 143 | /// 144 | /// The azure authentication endpoint. 145 | /// 146 | public string AzureAuthEndpoint { get; set; } 147 | 148 | /// 149 | /// Gets or sets the aad tenant. 150 | /// 151 | /// 152 | /// The aad tenant. 153 | /// 154 | public string AadTenant { get; set; } 155 | 156 | /// 157 | /// Gets or sets the use ADAL. 158 | /// 159 | /// 160 | /// The aad tenant. 161 | /// 162 | public bool UseADAL { get; set; } 163 | } 164 | 165 | /// 166 | /// 167 | /// 168 | /// 169 | public class Instances : BindingList 170 | { 171 | } 172 | 173 | /// 174 | /// 175 | /// 176 | [Serializable] 177 | public class JobGroup 178 | { 179 | /// 180 | /// Gets or sets the name. 181 | /// 182 | /// 183 | /// The name. 184 | /// 185 | public string Name { get; set; } 186 | } 187 | 188 | /// 189 | /// 190 | /// 191 | /// 192 | public class JobGroups : BindingList 193 | { 194 | } 195 | 196 | /// 197 | /// 198 | /// 199 | [Serializable] 200 | public class User 201 | { 202 | /// 203 | /// Gets or sets the login. 204 | /// 205 | /// 206 | /// The login. 207 | /// 208 | public string Login { get; set; } 209 | 210 | /// 211 | /// Gets or sets the password. 212 | /// 213 | /// 214 | /// The password. 215 | /// 216 | public string Password { get; set; } 217 | } 218 | 219 | /// 220 | /// 221 | /// 222 | /// 223 | public class Users : BindingList 224 | { 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /Scheduler/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Scheduler/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Server/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 |
6 | 7 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Server/Configuration.cs: -------------------------------------------------------------------------------- 1 | //https://github.com/quartznet/quartznet 2 | using System; 3 | using System.Collections.Specialized; 4 | using System.Configuration; 5 | using log4net; 6 | 7 | namespace RecurringIntegrationsScheduler.Server 8 | { 9 | /// 10 | /// Configuration for the Quartz server. 11 | /// 12 | public class Configuration 13 | { 14 | private static readonly ILog log = LogManager.GetLogger(typeof(Configuration)); 15 | 16 | private const string PrefixServerConfiguration = "quartz.server"; 17 | private const string KeyServiceName = PrefixServerConfiguration + ".serviceName"; 18 | private const string KeyServiceDisplayName = PrefixServerConfiguration + ".serviceDisplayName"; 19 | private const string KeyServiceDescription = PrefixServerConfiguration + ".serviceDescription"; 20 | private const string KeyServerImplementationType = PrefixServerConfiguration + ".type"; 21 | 22 | private const string DefaultServiceName = "QuartzServer"; 23 | private const string DefaultServiceDisplayName = "Quartz Server"; 24 | private const string DefaultServiceDescription = "Quartz Job Scheduling Server"; 25 | private static readonly string DefaultServerImplementationType = typeof(QuartzServer).AssemblyQualifiedName!; 26 | 27 | private static readonly NameValueCollection? configuration; 28 | 29 | /// 30 | /// Initializes the class. 31 | /// 32 | static Configuration() 33 | { 34 | try 35 | { 36 | configuration = (NameValueCollection) ConfigurationManager.GetSection("quartz"); 37 | } 38 | catch (Exception e) 39 | { 40 | log.Warn("could not read configuration using ConfigurationManager.GetSection: " + e.Message); 41 | } 42 | } 43 | 44 | /// 45 | /// Gets the name of the service. 46 | /// 47 | /// The name of the service. 48 | public static string ServiceName => GetConfigurationOrDefault(KeyServiceName, DefaultServiceName); 49 | 50 | /// 51 | /// Gets the display name of the service. 52 | /// 53 | /// The display name of the service. 54 | public static string ServiceDisplayName => GetConfigurationOrDefault(KeyServiceDisplayName, DefaultServiceDisplayName); 55 | 56 | /// 57 | /// Gets the service description. 58 | /// 59 | /// The service description. 60 | public static string ServiceDescription => GetConfigurationOrDefault(KeyServiceDescription, DefaultServiceDescription); 61 | 62 | /// 63 | /// Gets the type name of the server implementation. 64 | /// 65 | /// The type of the server implementation. 66 | public static string ServerImplementationType => GetConfigurationOrDefault(KeyServerImplementationType, DefaultServerImplementationType); 67 | 68 | /// 69 | /// Returns configuration value with given key. If configuration 70 | /// for the does not exists, return the default value. 71 | /// 72 | /// Key to read configuration with. 73 | /// Default value to return if configuration is not found 74 | /// The configuration value. 75 | private static string GetConfigurationOrDefault(string configurationKey, string defaultValue) 76 | { 77 | string? retValue = null; 78 | if (configuration != null) 79 | { 80 | retValue = configuration[configurationKey]; 81 | } 82 | 83 | if (retValue == null || retValue.Trim().Length == 0) 84 | { 85 | retValue = defaultValue; 86 | } 87 | return retValue; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Server/IQuartzServer.cs: -------------------------------------------------------------------------------- 1 | //https://github.com/quartznet/quartznet 2 | using System.Threading.Tasks; 3 | 4 | namespace RecurringIntegrationsScheduler.Server 5 | { 6 | /// 7 | /// Service interface for core Quartz.NET server. 8 | /// 9 | public interface IQuartzServer 10 | { 11 | /// 12 | /// Initializes the instance of . 13 | /// Initialization will only be called once in server's lifetime. 14 | /// 15 | Task Initialize(); 16 | 17 | /// 18 | /// Starts this instance. 19 | /// 20 | void Start(); 21 | 22 | /// 23 | /// Stops this instance. 24 | /// 25 | void Stop(); 26 | 27 | /// 28 | /// Pauses all activity in scheduler. 29 | /// 30 | void Pause(); 31 | 32 | /// 33 | /// Resumes all activity in server. 34 | /// 35 | void Resume(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Server/Program.cs: -------------------------------------------------------------------------------- 1 | //https://github.com/quartznet/quartznet 2 | using System; 3 | using System.IO; 4 | using System.Reflection; 5 | using log4net.Config; 6 | using Topshelf; 7 | 8 | namespace RecurringIntegrationsScheduler.Server 9 | { 10 | /// 11 | /// The server's main entry point. 12 | /// 13 | public static class Program 14 | { 15 | /// 16 | /// Main. 17 | /// 18 | public static void Main() 19 | { 20 | // change from service account's dir to more logical one 21 | Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); 22 | 23 | var logRepository = log4net.LogManager.GetRepository(Assembly.GetEntryAssembly()); 24 | XmlConfigurator.Configure(logRepository, new FileInfo("log4net.config")); 25 | 26 | HostFactory.Run(x => 27 | { 28 | x.RunAsLocalSystem(); 29 | 30 | x.SetDescription(Configuration.ServiceDescription); 31 | x.SetDisplayName(Configuration.ServiceDisplayName); 32 | x.SetServiceName(Configuration.ServiceName); 33 | 34 | x.Service(factory => 35 | { 36 | QuartzServer server = QuartzServerFactory.CreateServer(); 37 | server.Initialize().GetAwaiter().GetResult(); 38 | return server; 39 | }); 40 | }); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Server/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | 11 | [assembly: AssemblyTitle("Recurring Integrations Scheduler Server")] 12 | [assembly: AssemblyDescription("")] 13 | [assembly: AssemblyConfiguration("")] 14 | [assembly: AssemblyCompany("")] 15 | [assembly: AssemblyProduct("Recurring Integrations Scheduler")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Setting ComVisible to false makes the types in this assembly not visible 20 | // to COM components. If you need to access a type in this assembly from 21 | // COM, set the ComVisible attribute to true on that type. 22 | 23 | [assembly: ComVisible(false)] 24 | 25 | // The following GUID is for the ID of the typelib if this project is exposed to COM 26 | 27 | [assembly: Guid("a83c017e-6a29-4039-8970-46ab95bee619")] -------------------------------------------------------------------------------- /Server/QuartzServer.cs: -------------------------------------------------------------------------------- 1 | //https://github.com/quartznet/quartznet 2 | using System; 3 | using System.Threading.Tasks; 4 | using log4net; 5 | using Quartz; 6 | using Quartz.Impl; 7 | using Topshelf; 8 | 9 | namespace RecurringIntegrationsScheduler.Server 10 | { 11 | /// 12 | /// The main server logic. 13 | /// 14 | public class QuartzServer : ServiceControl, IQuartzServer 15 | { 16 | private readonly ILog logger; 17 | private ISchedulerFactory schedulerFactory = null!; 18 | private IScheduler scheduler = null!; 19 | 20 | /// 21 | /// Initializes a new instance of the class. 22 | /// 23 | public QuartzServer() 24 | { 25 | logger = LogManager.GetLogger(GetType()); 26 | } 27 | 28 | /// 29 | /// Initializes the instance of the class. 30 | /// 31 | public virtual async Task Initialize() 32 | { 33 | try 34 | { 35 | schedulerFactory = CreateSchedulerFactory(); 36 | scheduler = await GetScheduler().ConfigureAwait(false); 37 | } 38 | catch (Exception e) 39 | { 40 | logger.Error("Server initialization failed:" + e.Message, e); 41 | throw; 42 | } 43 | } 44 | 45 | /// 46 | /// Gets the scheduler with which this server should operate with. 47 | /// 48 | /// 49 | protected virtual Task GetScheduler() 50 | { 51 | return schedulerFactory.GetScheduler(); 52 | } 53 | 54 | /// 55 | /// Returns the current scheduler instance (usually created in 56 | /// using the method). 57 | /// 58 | protected virtual IScheduler Scheduler => scheduler; 59 | 60 | /// 61 | /// Creates the scheduler factory that will be the factory 62 | /// for all schedulers on this instance. 63 | /// 64 | /// 65 | protected virtual ISchedulerFactory CreateSchedulerFactory() 66 | { 67 | return new StdSchedulerFactory(); 68 | } 69 | 70 | /// 71 | /// Starts this instance, delegates to scheduler. 72 | /// 73 | public virtual void Start() 74 | { 75 | try 76 | { 77 | scheduler.Start(); 78 | } 79 | catch (Exception ex) 80 | { 81 | logger.Fatal($"Scheduler start failed: {ex.Message}", ex); 82 | throw; 83 | } 84 | 85 | logger.Info("Scheduler started successfully"); 86 | } 87 | 88 | /// 89 | /// Stops this instance, delegates to scheduler. 90 | /// 91 | public virtual void Stop() 92 | { 93 | try 94 | { 95 | scheduler.Shutdown(true); 96 | } 97 | catch (Exception ex) 98 | { 99 | logger.Error($"Scheduler stop failed: {ex.Message}", ex); 100 | throw; 101 | } 102 | 103 | logger.Info("Scheduler shutdown complete"); 104 | } 105 | 106 | /// 107 | /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. 108 | /// 109 | public virtual void Dispose() 110 | { 111 | // no-op for now 112 | } 113 | 114 | /// 115 | /// Pauses all activity in scheduler. 116 | /// 117 | public virtual void Pause() 118 | { 119 | scheduler.PauseAll(); 120 | } 121 | 122 | /// 123 | /// Resumes all activity in server. 124 | /// 125 | public void Resume() 126 | { 127 | scheduler.ResumeAll(); 128 | } 129 | 130 | /// 131 | /// TopShelf's method delegated to . 132 | /// 133 | public bool Start(HostControl hostControl) 134 | { 135 | Start(); 136 | return true; 137 | } 138 | 139 | /// 140 | /// TopShelf's method delegated to . 141 | /// 142 | public bool Stop(HostControl hostControl) 143 | { 144 | Stop(); 145 | return true; 146 | } 147 | 148 | /// 149 | /// TopShelf's method delegated to . 150 | /// 151 | public bool Pause(HostControl hostControl) 152 | { 153 | Pause(); 154 | return true; 155 | } 156 | 157 | /// 158 | /// TopShelf's method delegated to . 159 | /// 160 | public bool Continue(HostControl hostControl) 161 | { 162 | Resume(); 163 | return true; 164 | } 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /Server/QuartzServerFactory.cs: -------------------------------------------------------------------------------- 1 | //https://github.com/quartznet/quartznet 2 | 3 | using System; 4 | using log4net; 5 | 6 | namespace RecurringIntegrationsScheduler.Server 7 | { 8 | /// 9 | /// Factory class to create Quartz server implementations from. 10 | /// 11 | public class QuartzServerFactory 12 | { 13 | private static readonly ILog logger = LogManager.GetLogger(typeof (QuartzServerFactory)); 14 | 15 | /// 16 | /// Creates a new instance of an Quartz.NET server core. 17 | /// 18 | /// 19 | public static QuartzServer CreateServer() 20 | { 21 | string typeName = Configuration.ServerImplementationType; 22 | 23 | Type t = Type.GetType(typeName, true)!; 24 | 25 | logger.Debug("Creating new instance of server type '" + typeName + "'"); 26 | QuartzServer retValue = (QuartzServer) Activator.CreateInstance(t)!; 27 | logger.Debug("Instance successfully created"); 28 | return retValue; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Server/Schedule.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | true 6 | 7 | 8 | -------------------------------------------------------------------------------- /Server/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Setup/README.md: -------------------------------------------------------------------------------- 1 | **Recurring Integrations Scheduler.iss** file contains packaging instructions for [Inno Setup](http://www.jrsoftware.org/isinfo.php) installer. 2 | 3 | Example command to create installation package (executed from root folder of the solution): 4 | ``` 5 | "C:\Program Files (x86)\Inno Setup 6\Compil32.exe" /cc ".\Setup\Recurring Integrations Scheduler.iss" 6 | ``` 7 | -------------------------------------------------------------------------------- /Setup/Recurring Integrations Scheduler.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Setup/Recurring Integrations Scheduler.ico -------------------------------------------------------------------------------- /Third Party Notices.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/microsoft/Recurring-Integrations-Scheduler/b6f09b6671e46490e3b5fcd192ce1ae4cc6593a5/Third Party Notices.txt -------------------------------------------------------------------------------- /Version.cs: -------------------------------------------------------------------------------- 1 | /* Copyright (c) Microsoft Corporation. All rights reserved. 2 | Licensed under the MIT License. */ 3 | 4 | using System.Reflection; 5 | [assembly: AssemblyVersion("3.5.0.0")] 6 | --------------------------------------------------------------------------------