├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .vscode ├── launch.json └── tasks.json ├── ApigeeToApimMigrationTool.DataAccess ├── ApigeeToApimMigrationTool.DataAccess.csproj └── ProxyMetaDataDataAccess.cs ├── ApigeeToApimMigrationTool.Database ├── ApigeeToApimMigrationTool.Database.sqlproj ├── StoredProcedures │ └── DeleteAllData.sql └── Tables │ ├── Proxy.sql │ ├── ProxyBasePath.sql │ ├── ProxyEndpoint.sql │ └── ProxyPolicy.sql ├── ApigeeToApimMigrationTool.Test ├── ApigeeToApimMigrationTool.Test.csproj ├── AzureApimServiceTest.cs ├── AzureSdkMocks │ ├── MockApiManagementProductResource.cs │ └── MockApiResource.cs ├── BundleLoaderTests.cs ├── MockApigeeManagementApiService.cs ├── MockApigeeXmlLoader.cs ├── MockApimProvider.cs ├── MockBundleProvider.cs ├── MockPolicyTransformer.cs ├── TestBundles │ ├── GetSharedFlow │ │ └── sharedflowbundle │ │ │ ├── GetSharedFlow.xml │ │ │ ├── policies │ │ │ └── Get-Shared-Flow.xml │ │ │ └── sharedflows │ │ │ └── default.xml │ └── Test-API │ │ └── apiproxy │ │ ├── Test-API.xml │ │ ├── policies │ │ └── SharedFlowCallout.xml │ │ ├── proxies │ │ └── default.xml │ │ └── targets │ │ └── default.xml ├── TransformationTests │ └── AssignMessageTransformationTests.cs └── Usings.cs ├── ApigeeToApimMigrationTool.sln ├── ApigeeToApimMigrationTool ├── ApigeeToApimMigrationTool.csproj ├── Program.cs ├── Properties │ └── launchSettings.json └── apigeeToApimConfig.SAMPLE.json ├── ApigeeToAzureApimMigrationTool.Core ├── ApigeeToAzureApimMigrationTool.Core.csproj ├── Config │ ├── ApigeeConfiguration.cs │ ├── ApigeeConfigurationBinder.cs │ ├── ApimConfiguration.cs │ ├── ApimConfigurationBinder.cs │ ├── EntraConfiguration.cs │ └── EntraConfigurationBinder.cs ├── Dto │ ├── ApiProductMetaData.cs │ ├── ApiProxyEndpointMetadata.cs │ ├── ApiProxyMetaData.cs │ ├── ApiProxyRevisionMetadata.cs │ ├── ApigeeEntityModel.cs │ ├── ApigeeTargetServerModel.cs │ ├── ApimConfig.cs │ ├── AuthToken.cs │ ├── AzureCredentials.cs │ └── KeyValueMapModel.cs ├── Enum │ ├── AssignMessagePolicyOperations.cs │ └── PolicyDirection.cs └── Interface │ ├── IApigeeManagementApiService.cs │ ├── IApigeeXmlLoader.cs │ ├── IApimPolicyTransformer.cs │ ├── IApimProvider.cs │ ├── IAzureApimService.cs │ ├── IBundle.cs │ ├── IBundleProvider.cs │ ├── IExpressionTranslator.cs │ ├── IPolicyTransformation.cs │ ├── IPolicyTransformationFactory.cs │ └── IProxyMetaDataDataAccess.cs ├── ApigeeToAzureApimMigrationTool.Logic ├── ApigeeManagementApiService.cs ├── ApigeeManagementApiTestFileService.cs ├── ApigeeToApimPolicyTransformer.cs ├── ApigeeToAzureApimMigrationTool.Service.csproj ├── ApigeeXmlFileLoader.cs ├── AzureApimProvider.cs ├── AzureApimService.cs ├── Bundles │ ├── ApigeeFileApiProxyBundle.cs │ ├── ApigeeFileBundleProvider.cs │ ├── ApigeeFileSharedFlowBundle.cs │ ├── ApigeeOnlineApiProxyBundle.cs │ ├── ApigeeOnlineBundleProvider.cs │ └── ApigeeOnlineSharedFlowBundle.cs ├── ExpressionTranslator.cs └── Transformations │ ├── AccessControlTransformation.cs │ ├── AssignMessageTransformation.cs │ ├── BasicAuthenticationTransformation.cs │ ├── ExtractVariablesTransformation.cs │ ├── FlowCalloutTransformation.cs │ ├── InvalidateCacheTransformation.cs │ ├── KeyValueMapTransformation.cs │ ├── LookupCacheTransformation.cs │ ├── NullTransformation.cs │ ├── OAuthV2Transformation.cs │ ├── PolicyTransformationFactory.cs │ ├── PopulateCacheTransformation.cs │ ├── RaiseFaultTransformation.cs │ ├── ServiceCalloutTransformation.cs │ ├── SpikeArrestTransformation.cs │ └── VerifyJwtTransformation.cs ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── Roadmap.md ├── SECURITY.md └── SUPPORT.md /.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 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | 400 | **/credentials.json 401 | **/appsettings.json 402 | *.dccache 403 | /ApigeeToApimMigrationTool/Properties/launchSettings.json 404 | /ApigeeToApimMigrationTool/apigeeToApimConfig.json 405 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | // Use IntelliSense to find out which attributes exist for C# debugging 6 | // Use hover for the description of the existing attributes 7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/ApigeeToApimMigrationTool/bin/Debug/net6.0/ApigeeToApimMigrationTool.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/ApigeeToApimMigrationTool", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach" 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/ApigeeToApimMigrationTool/ApigeeToApimMigrationTool.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/ApigeeToApimMigrationTool/ApigeeToApimMigrationTool.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "--project", 36 | "${workspaceFolder}/ApigeeToApimMigrationTool/ApigeeToApimMigrationTool.csproj" 37 | ], 38 | "problemMatcher": "$msCompile" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.DataAccess/ApigeeToApimMigrationTool.DataAccess.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.DataAccess/ProxyMetaDataDataAccess.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core; 2 | using ApigeeToApimMigrationTool.Core.Interface; 3 | using ApigeeToAzureApimMigrationTool.Core.Dto; 4 | using ApigeeToAzureApimMigrationTool.Core.Interface; 5 | using Dapper; 6 | using Microsoft.Data.SqlClient; 7 | using System.IO; 8 | 9 | namespace ApigeeToApimMigrationTool.DataAccess 10 | { 11 | 12 | public class ProxyMetaDataDataAccess : IProxyMetaDataDataAccess 13 | { 14 | private readonly SqlConnection _connection; 15 | public ProxyMetaDataDataAccess(string connectionString) 16 | { 17 | _connection = new SqlConnection(connectionString); 18 | } 19 | 20 | public async Task IsProxyMetadataTablePopulted() 21 | { 22 | bool result = await _connection.QuerySingleOrDefaultAsync("if EXISTS (SELECT 1 FROM Proxy) begin select 1 end else begin select 0 end\r\n"); 23 | return result; 24 | } 25 | 26 | public async Task GetProxyRevisionMetaDataByPath(string path) 27 | { 28 | var result = await _connection.QuerySingleOrDefaultAsync($@"select p.Id, p.ProxyName, p.Revision, p.Description from Proxy p 29 | join ProxyBasePath bp on p.Id = bp.ProxyId where bp.Path = '{path}'"); 30 | return result; 31 | } 32 | 33 | public async Task InsertProxyRevisionMetaData(ApiProxyRevisionMetadata metaData) 34 | { 35 | if (_connection.State == System.Data.ConnectionState.Closed) 36 | await _connection.OpenAsync(); 37 | 38 | SqlTransaction transaction = _connection.BeginTransaction(); 39 | try 40 | { 41 | SqlCommand apiProxyInsertCommand = new SqlCommand($@"INSERT INTO [Proxy]([ProxyName],[Revision],[Description]) 42 | VALUES (@Name,@Revision, @Description); select SCOPE_IDENTITY()", _connection, transaction); 43 | apiProxyInsertCommand.Parameters.AddWithValue("Name", metaData.Name); 44 | apiProxyInsertCommand.Parameters.AddWithValue("Revision", metaData.Revision); 45 | apiProxyInsertCommand.Parameters.AddWithValue("Description", metaData.Description?? ""); 46 | var proxyId = await apiProxyInsertCommand.ExecuteScalarAsync(); 47 | 48 | if (proxyId == null) 49 | throw new ArgumentNullException("proxyId", "proxyId is null after inserting the metadata into sql table Proxy"); 50 | 51 | foreach (var path in metaData.BasePaths) 52 | { 53 | SqlCommand apiProxyPathInsertCommand = new SqlCommand($"INSERT INTO [ProxyBasePath]([Path],[ProxyId]) VALUES (@Path ,@ProxyId)", _connection, transaction); 54 | apiProxyPathInsertCommand.Parameters.AddWithValue("Path", path?? ""); 55 | apiProxyPathInsertCommand.Parameters.AddWithValue("ProxyId", proxyId); 56 | await apiProxyPathInsertCommand.ExecuteNonQueryAsync(); 57 | } 58 | foreach (var policy in metaData.Policies) 59 | { 60 | SqlCommand apiProxyPolicyInsertCommand = new SqlCommand($"INSERT INTO [ProxyPolicy]([PolicyName],[ProxyId]) VALUES (@PolicyName ,@ProxyId)", _connection, transaction); 61 | apiProxyPolicyInsertCommand.Parameters.AddWithValue("PolicyName", policy ?? ""); 62 | apiProxyPolicyInsertCommand.Parameters.AddWithValue("ProxyId", proxyId); 63 | 64 | await apiProxyPolicyInsertCommand.ExecuteNonQueryAsync(); 65 | } 66 | foreach (var endpoint in metaData.ProxyEndpointPaths) 67 | { 68 | SqlCommand apiProxyProxyEndpointsInsertCommand = new SqlCommand($"INSERT INTO [ProxyEndpoint]([ProxyEndpoint],[ProxyId],[EndpointName]) VALUES (@ProxyEndpoint ,@ProxyId, @EndpointName)", _connection, transaction); 69 | apiProxyProxyEndpointsInsertCommand.Parameters.AddWithValue("EndpointName", endpoint.Key ?? ""); 70 | apiProxyProxyEndpointsInsertCommand.Parameters.AddWithValue("ProxyEndpoint", endpoint.Value ?? ""); 71 | apiProxyProxyEndpointsInsertCommand.Parameters.AddWithValue("ProxyId", proxyId); 72 | await apiProxyProxyEndpointsInsertCommand.ExecuteNonQueryAsync(); 73 | } 74 | 75 | await transaction.CommitAsync(); 76 | } 77 | catch (Exception) 78 | { 79 | await transaction.RollbackAsync(); 80 | throw; 81 | } 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Database/ApigeeToApimMigrationTool.Database.sqlproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | ApigeeToApimMigrationTool.Database 7 | 2.0 8 | 4.1 9 | {0fb5c73e-88dd-44de-9dd5-882d6de57189} 10 | Microsoft.Data.Tools.Schema.Sql.SqlAzureV12DatabaseSchemaProvider 11 | Database 12 | 13 | 14 | ApigeeToApimMigrationTool.Database 15 | ApigeeToApimMigrationTool.Database 16 | 1033, CI 17 | BySchemaAndSchemaType 18 | True 19 | v4.7.2 20 | CS 21 | Properties 22 | False 23 | True 24 | True 25 | 26 | 27 | bin\Release\ 28 | $(MSBuildProjectName).sql 29 | False 30 | pdbonly 31 | true 32 | false 33 | true 34 | prompt 35 | 4 36 | 37 | 38 | bin\Debug\ 39 | $(MSBuildProjectName).sql 40 | false 41 | true 42 | full 43 | false 44 | true 45 | true 46 | prompt 47 | 4 48 | 49 | 50 | 11.0 51 | 52 | True 53 | 11.0 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Database/StoredProcedures/DeleteAllData.sql: -------------------------------------------------------------------------------- 1 | CREATE PROCEDURE [dbo].[DeleteAllData] 2 | AS 3 | delete from ProxyPolicy 4 | delete from ProxyBasePath 5 | delete from ProxyEndpoint 6 | delete from Proxy 7 | RETURN 0 8 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Database/Tables/Proxy.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[Proxy]( 2 | [Id] [int] IDENTITY(1,1) NOT NULL, 3 | [ProxyName] [varchar](150) NOT NULL, 4 | [Revision] [smallint] NOT NULL, 5 | [Description] [varchar](1000) NULL, 6 | CONSTRAINT [PK_Proxy] PRIMARY KEY CLUSTERED 7 | ( 8 | [Id] ASC 9 | )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] 10 | ) ON [PRIMARY] 11 | GO -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Database/Tables/ProxyBasePath.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ProxyBasePath] 2 | ( 3 | [Id] INT NOT NULL PRIMARY KEY IDENTITY, 4 | [Path] VARCHAR(1000) NOT NULL, 5 | [ProxyId] INT NOT NULL, 6 | CONSTRAINT [FK_ProxyBasePath_ToProxy] FOREIGN KEY ([ProxyId]) REFERENCES [Proxy]([Id]) 7 | ) 8 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Database/Tables/ProxyEndpoint.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ProxyEndpoint] 2 | ( 3 | [Id] INT NOT NULL PRIMARY KEY IDENTITY, 4 | [ProxyEndpoint] VARCHAR(100) NOT NULL, 5 | [ProxyId] INT NOT NULL, 6 | [EndpointName] NVARCHAR(100) NOT NULL, 7 | CONSTRAINT [FK_ProxyEndpoint_ToProxy] FOREIGN KEY ([ProxyId]) REFERENCES [Proxy]([Id]) 8 | ) 9 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Database/Tables/ProxyPolicy.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE [dbo].[ProxyPolicy] 2 | ( 3 | [Id] INT NOT NULL PRIMARY KEY IDENTITY, 4 | [PolicyName] VARCHAR(100) NOT NULL, 5 | [ProxyId] INT NOT NULL, 6 | CONSTRAINT [FK_ProxyPolicy_ToProxy] FOREIGN KEY ([ProxyId]) REFERENCES [Proxy]([Id]) 7 | ) 8 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/ApigeeToApimMigrationTool.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | Always 33 | 34 | 35 | Always 36 | 37 | 38 | Always 39 | 40 | 41 | Always 42 | 43 | 44 | Always 45 | 46 | 47 | Always 48 | 49 | 50 | Always 51 | 52 | 53 | Always 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/AzureApimServiceTest.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using ApigeeToAzureApimMigrationTool.Core; 3 | using ApigeeToAzureApimMigrationTool.Core.Dto; 4 | using ApigeeToAzureApimMigrationTool.Core.Interface; 5 | using ApigeeToAzureApimMigrationTool.Service; 6 | using ApigeeToAzureApimMigrationTool.Service.Transformations; 7 | using Newtonsoft.Json; 8 | using System.Reflection; 9 | using System.Xml.Linq; 10 | 11 | namespace ApigeeToApimMigrationTool.Test 12 | { 13 | public class AzureApimServiceTest 14 | { 15 | private IAzureApimService _azureApimServiceUnderTest; 16 | 17 | // Dependencies 18 | private readonly MockApigeeManagementApiService _mockApigeeManagementApiService; 19 | private readonly MockApigeeXmlLoader _mockApigeeXmlLoader; 20 | private readonly MockApimProvider _mockApimProvider; 21 | private readonly MockBundleProvider _mockBundleProvider; 22 | private readonly IExpressionTranslator _expressionTranslator; 23 | 24 | public AzureApimServiceTest() 25 | { 26 | _mockApigeeManagementApiService = new MockApigeeManagementApiService(); 27 | _mockApigeeXmlLoader = new MockApigeeXmlLoader(); 28 | _mockApimProvider = new MockApimProvider(); 29 | _mockBundleProvider = new MockBundleProvider(); 30 | _expressionTranslator = new ExpressionTranslator(); 31 | 32 | // We need a real transformation factory and transformer here because we are testing the integration between the service 33 | // and the transformer 34 | var policyTransformationFactory = new PolicyTransformationFactory( 35 | _mockApigeeManagementApiService, _mockApimProvider, _mockBundleProvider, _mockApigeeXmlLoader, _expressionTranslator); 36 | 37 | var policyTransformer = new ApigeeToApimPolicyTransformer(policyTransformationFactory, _mockApimProvider, _expressionTranslator); 38 | 39 | _azureApimServiceUnderTest = new AzureApimService( 40 | apimProvider: _mockApimProvider, 41 | apigeeXmlLoader: _mockApigeeXmlLoader, 42 | policyTransformer: policyTransformer, 43 | expressionTranslator: _expressionTranslator); 44 | 45 | SetupDefaultPolicies(); 46 | } 47 | 48 | [Fact] 49 | public async Task AssignMessagePolicy_InRequestPreFlow_WithSetHeader_SetsApimHeader_InInbound() 50 | { 51 | XElement testPolicyElement = XElement.Parse(@"test-policy"); 52 | XElement? preFlowPolicyElement = _mockApigeeXmlLoader.ProxyEndpointXml["default"].Element("ProxyEndpoint")?.Element("PreFlow"); 53 | 54 | Assert.NotNull(preFlowPolicyElement); 55 | preFlowPolicyElement?.Add(testPolicyElement); 56 | 57 | _mockApigeeXmlLoader.PolicyXml.Add("test-policy", XDocument.Parse( 58 | @" 59 | 60 | Set Header 61 | 62 | 63 | 64 | 65 |
test-value
66 |
67 |
68 | true 69 | 70 |
")); 71 | 72 | await _azureApimServiceUnderTest.ImportApi( 73 | apimName: "testApi", 74 | proxyName: "Test-Api", 75 | apimConfiguration: new ApimConfiguration(), 76 | apigeeConfiguration: new ApigeeConfiguration(), 77 | keyVaultName: string.Empty); 78 | 79 | var policy = _mockApimProvider.PolicyXml; 80 | 81 | XElement? setHeaderPolicy = policy?.Element("policies")?.Element("inbound")?.Element("set-header"); 82 | Assert.NotNull(setHeaderPolicy); 83 | Assert.Equal("test-header", setHeaderPolicy?.Attribute("name")?.Value); 84 | Assert.Equal("test-value", setHeaderPolicy?.Element("value")?.Value); 85 | 86 | } 87 | 88 | private void SetupDefaultPolicies() 89 | { 90 | _mockApigeeXmlLoader.ProxyXml.Add("Test-Api", XDocument.Parse( 91 | @" 92 | 93 | /test 94 | Test API 95 | Test-API 96 | 97 | test-policy 98 | 99 | 100 | default 101 | 102 | 103 | 104 | 105 | 106 | default 107 | 108 | ")); 109 | 110 | _mockApigeeXmlLoader.ProxyEndpointXml.Add("default", XDocument.Parse( 111 | @" 112 | 113 | 114 | 115 | 116 | 117 | /test 118 | 119 | 120 | default 121 | 122 | ")); 123 | 124 | _mockApigeeXmlLoader.TargetXml.Add("default", XDocument.Parse( 125 | @" 126 | 127 | 128 | 129 | 130 | 131 | https://test.example.com 132 | 133 | ")); 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/AzureSdkMocks/MockApiManagementProductResource.cs: -------------------------------------------------------------------------------- 1 | using Azure.ResourceManager.ApiManagement; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ApigeeToApimMigrationTool.Test.AzureSdkMocks 9 | { 10 | public class MockApiManagementProductResource : ApiManagementProductResource 11 | { 12 | public MockApiManagementProductResource() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/AzureSdkMocks/MockApiResource.cs: -------------------------------------------------------------------------------- 1 | using Azure.ResourceManager.ApiManagement; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ApigeeToApimMigrationTool.Test.AzureSdkMocks 9 | { 10 | public class MockApiResource : ApiResource 11 | { 12 | public MockApiResource() 13 | { 14 | 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/BundleLoaderTests.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using ApigeeToAzureApimMigrationTool.Service; 4 | using ApigeeToAzureApimMigrationTool.Service.Bundles; 5 | using ApigeeToAzureApimMigrationTool.Service.Transformations; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Linq; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | 12 | namespace ApigeeToApimMigrationTool.Test 13 | { 14 | public class BundleLoaderTests 15 | { 16 | // The purpose of these tests is to test loading bundles from files. 17 | // We're using the test file loaders, but the process should be the same 18 | // with the real loaders, only we'll be loading from the filesystem instead 19 | // of from the Apigee API. 20 | 21 | private readonly AzureApimService _azureApimServiceUnderTest; 22 | private readonly IBundleProvider _bundleProvider; 23 | private readonly MockApimProvider _apimProvider; 24 | private readonly IExpressionTranslator _expressionTranslator; 25 | 26 | public BundleLoaderTests() 27 | { 28 | var testConfigPath = "TestBundles"; 29 | 30 | _bundleProvider = new ApigeeFileBundleProvider(testConfigPath); 31 | _apimProvider = new MockApimProvider(); 32 | _expressionTranslator = new ExpressionTranslator(); 33 | 34 | 35 | IApigeeXmlLoader apigeeXmlLoader = new ApigeeXmlFileLoader(_bundleProvider); 36 | IExpressionTranslator expressionTranslator = new ExpressionTranslator(); 37 | IApigeeManagementApiService apigeeManagementApiService = new ApigeeManagementApiTestFileService(_bundleProvider, apigeeXmlLoader, testConfigPath); 38 | IPolicyTransformationFactory policyTransformationFactory = new PolicyTransformationFactory(apigeeManagementApiService, _apimProvider, _bundleProvider, apigeeXmlLoader, expressionTranslator); 39 | 40 | _azureApimServiceUnderTest = new AzureApimService( 41 | apigeeXmlLoader: apigeeXmlLoader, 42 | apimProvider: _apimProvider, 43 | expressionTranslator: _expressionTranslator, 44 | policyTransformer: new ApigeeToApimPolicyTransformer(policyTransformationFactory, _apimProvider, _expressionTranslator)); 45 | } 46 | [Fact] 47 | public async Task FileBundleLoader_WithSharedFlowPolicy_LoadsSharedFlow() 48 | { 49 | var bundle = _bundleProvider.GetApiProxyBundle("Test-API"); 50 | await bundle.LoadBundle(); 51 | await _azureApimServiceUnderTest.ImportApi("Test-Apim", "Test-API", null, null, null); 52 | 53 | // GetSharedFlow is a shared flow that is used by the Test-API proxy 54 | Assert.True(_apimProvider.PolicyFragments.ContainsKey("GetSharedFlow")); 55 | 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/MockApigeeManagementApiService.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.dto; 3 | using ApigeeToAzureApimMigrationTool.Core.Dto; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace ApigeeToApimMigrationTool.Test 11 | { 12 | public class MockApigeeManagementApiService : IApigeeManagementApiService 13 | { 14 | 15 | public string AuthenticationToken { get; set; } 16 | public string Username { get; set; } 17 | public string Password { get; set; } 18 | 19 | public string? Environment { get; private set; } 20 | 21 | public string ProxyName { get; set; } 22 | 23 | public Task DownloadApiProxyBundle(string basePath, string proxyName, int revision) 24 | 25 | { 26 | return Task.FromResult(string.Empty); 27 | } 28 | 29 | public Task DownloadSharedFlowBundle(string basePath, string sharedFlowName, int revision) 30 | { 31 | return Task.FromResult(string.Empty); 32 | } 33 | 34 | public Task GetApiProductByName(string productName) 35 | { 36 | var apiProduct = new ApiProductMetaData 37 | { 38 | Name = productName, 39 | DisplayName = productName, 40 | Description = "test", 41 | Proxies = new string[] { "test" } 42 | }; 43 | 44 | return Task.FromResult(apiProduct); 45 | } 46 | 47 | public Task GetApiProxyByName(string proxyName) 48 | { 49 | var apiProxy = new ApigeeEntityModel 50 | { 51 | name = proxyName, 52 | revision = new string[] { "1" }, 53 | metaData = new ApiProxyMetaData 54 | { 55 | createdAt = 0, 56 | createdBy = "test", 57 | lastModifiedAt = 0, 58 | lastModifiedBy = "test", 59 | subType = "test" 60 | }, 61 | }; 62 | 63 | return Task.FromResult(apiProxy); 64 | } 65 | 66 | public Task GetAuthenticationToken(string oneTimeToken, string authenticationBaseUrl) 67 | { 68 | return Task.FromResult(string.Empty); 69 | } 70 | 71 | public Task GetAuthenticationToken(string username, string password, string authenticationBaseUrl) 72 | { 73 | return Task.FromResult(string.Empty); 74 | } 75 | 76 | public Task GetKeyValueMapByName(string proxyName, string environment, string mapIdentifier) 77 | { 78 | var keyMap = new KeyValueMapModel 79 | { 80 | Name = mapIdentifier, 81 | Encrypted = false, 82 | Entry = new KeyValueMapItemModel[] 83 | { 84 | new KeyValueMapItemModel 85 | { 86 | Name = "test", 87 | Value = "test" 88 | } 89 | } 90 | }; 91 | 92 | return Task.FromResult(keyMap); 93 | 94 | } 95 | 96 | public Task GetSharedFlowByName(string sharedFlowName) 97 | { 98 | var sharedFlow = new ApigeeEntityModel { name = sharedFlowName, revision = new string[] { "1" } }; 99 | return Task.FromResult(sharedFlow); 100 | } 101 | 102 | public Task GetTargetServerByName(string targetServerName, string environment) 103 | { 104 | var targetServer = new ApigeeTargetServerModel 105 | { 106 | Name = targetServerName, 107 | Host = "test", 108 | Port = 443, 109 | IsEnabled = true, 110 | SSLInfo = new ApigeeTargetServerSSLInfo 111 | { 112 | Ciphers = new string[] { "test" }, 113 | ClientAuthEnabled = true, 114 | Enabled = true, 115 | IgnoreValidationError = true, 116 | Protocols = new string[] { "test" }, 117 | TrustStore = "test" 118 | } 119 | }; 120 | 121 | return Task.FromResult(targetServer); 122 | } 123 | 124 | public Task PopulateProxyReferenceDatabase() 125 | { 126 | return Task.CompletedTask; 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/MockApigeeXmlLoader.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Interface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Linq; 8 | 9 | namespace ApigeeToApimMigrationTool.Test 10 | { 11 | public class MockApigeeXmlLoader : IApigeeXmlLoader 12 | { 13 | public Dictionary PolicyXml { get; set; } = new Dictionary(); 14 | public Dictionary ProxyEndpointXml { get; set; } = new Dictionary(); 15 | public Dictionary ProxyXml { get; set; } = new Dictionary(); 16 | public Dictionary SharedFlowBundleXml { get; set; } = new Dictionary(); 17 | public Dictionary SharedFlowPolicyXml { get; set; } = new Dictionary(); 18 | public Dictionary SharedFlowXml { get; set; } = new Dictionary(); 19 | public Dictionary TargetXml { get; set; } = new Dictionary(); 20 | 21 | public XDocument LoadPolicyXml(string proxyName, string policyName) 22 | { 23 | if (PolicyXml.ContainsKey(policyName)) 24 | { 25 | return PolicyXml[policyName]; 26 | } 27 | else 28 | { 29 | return new XDocument(); 30 | } 31 | } 32 | 33 | public XDocument LoadProxyEndpointXml(string proxyName, string proxyEndpointName) 34 | { 35 | if (ProxyEndpointXml.ContainsKey(proxyEndpointName)) 36 | { 37 | return ProxyEndpointXml[proxyEndpointName]; 38 | } 39 | else 40 | { 41 | return new XDocument(); 42 | } 43 | } 44 | 45 | public XDocument LoadProxyXml(string proxyName) 46 | { 47 | if (ProxyXml.ContainsKey(proxyName)) 48 | { 49 | return ProxyXml[proxyName]; 50 | } 51 | else 52 | { 53 | return new XDocument(); 54 | } 55 | } 56 | 57 | public XDocument LoadSharedFlowBundleXml(string sharedFlowName) 58 | { 59 | if (SharedFlowBundleXml.ContainsKey(sharedFlowName)) 60 | { 61 | return SharedFlowBundleXml[sharedFlowName]; 62 | } 63 | else 64 | { 65 | return new XDocument(); 66 | } 67 | } 68 | 69 | public XDocument LoadSharedFlowPolicyXml(string sharedFlowName, string policyName) 70 | { 71 | if (SharedFlowPolicyXml.ContainsKey(policyName)) 72 | { 73 | return SharedFlowPolicyXml[policyName]; 74 | } 75 | else 76 | { 77 | return new XDocument(); 78 | } 79 | } 80 | 81 | public XDocument LoadSharedFlowXml(string sharedFlowName, string sharedFlowFileName) 82 | { 83 | if (SharedFlowXml.ContainsKey(sharedFlowName)) 84 | { 85 | return SharedFlowXml[sharedFlowName]; 86 | } 87 | else 88 | { 89 | return new XDocument(); 90 | } 91 | } 92 | 93 | public XDocument LoadTargetXml(string proxyName, string targetEndpointName) 94 | { 95 | if (TargetXml.ContainsKey(targetEndpointName)) 96 | { 97 | return TargetXml[targetEndpointName]; 98 | } 99 | else 100 | { 101 | return new XDocument(); 102 | } 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/MockApimProvider.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Test.AzureSdkMocks; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using Azure.ResourceManager.ApiManagement; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | namespace ApigeeToApimMigrationTool.Test 12 | { 13 | public class MockApimProvider : IApimProvider 14 | { 15 | public string ApimName { get; set; } 16 | public string ApimUrl { get; set; } 17 | public XDocument PolicyXml { get; private set; } = new XDocument(); 18 | public IDictionary PolicyFragments { get; private set; } = new Dictionary(); 19 | 20 | public Task AddApiToProduct(string apiId) 21 | { 22 | return Task.CompletedTask; 23 | } 24 | 25 | public Task AddNamedValue(string apimName, string proxyName, string mapIdentifier, string keyName, bool isSecret, string value, int index) 26 | { 27 | return Task.CompletedTask; 28 | } 29 | 30 | public Task CreateApi(string apiName, string apiDisplayName, string apiDescription, string apimName, string revision, string apiPath, string backendUrl, string? oauthConfigurationName) 31 | { 32 | var mockResource = new MockApiResource(); 33 | return Task.FromResult(mockResource as ApiResource); 34 | } 35 | 36 | public Task CreateOrUpdateOperation(string apiName, string description, string httpVerb) 37 | { 38 | return Task.CompletedTask; 39 | } 40 | 41 | public Task CreateOrUpdateOperationPolicy(XDocument operationPolicyXml, string operationName, string operationDescription, string httpVerb, string proxyPath) 42 | { 43 | return Task.CompletedTask; 44 | } 45 | 46 | public Task CreatePolicy(XDocument policyXml) 47 | { 48 | PolicyXml = policyXml; 49 | return Task.CompletedTask; 50 | } 51 | 52 | public Task CreatePolicyFragment(string policyFragmentName, string apimName, string policyFragmentXml, string policyFragmentDescription) 53 | { 54 | PolicyFragments.Add(policyFragmentName, XDocument.Parse(policyFragmentXml)); 55 | return Task.CompletedTask; 56 | } 57 | 58 | public Task CreateProduct(string name, string displayName, string description, string apimName) 59 | { 60 | var mockProduct = new MockApiManagementProductResource(); 61 | return Task.FromResult(mockProduct as ApiManagementProductResource); 62 | } 63 | 64 | public string RemoveTrailingSpecialCharacters(string input) 65 | { 66 | throw new NotImplementedException(); 67 | } 68 | 69 | public Task UpdateApiSubscriptionSetting(string apimName, string proxyName, string headerName = "", string queryParameterName = "") 70 | { 71 | return Task.CompletedTask; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/MockBundleProvider.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Interface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ApigeeToApimMigrationTool.Test 9 | { 10 | public class MockBundleProvider : IBundleProvider 11 | { 12 | public IBundle GetApiProxyBundle(string proxyOrProductName) 13 | { 14 | throw new NotImplementedException(); 15 | } 16 | 17 | public IBundle GetSharedFlowBundle(string sharedFlowName) 18 | { 19 | throw new NotImplementedException(); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/MockPolicyTransformer.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToApimMigrationTool.Test 11 | { 12 | public class MockPolicyTransformer : IApimPolicyTransformer 13 | { 14 | public Task TransformPoliciesInCollection(IEnumerable? elements, XElement azureApimPolicySection, Func xmlLoader, 15 | string apimName, string proxyName, ApigeeConfiguration apigeeConfiguration, ApimConfiguration apimConfig) 16 | { 17 | throw new NotImplementedException(); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TestBundles/GetSharedFlow/sharedflowbundle/GetSharedFlow.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 1513011664744 5 | jaspecla@microsoft.com 6 | Test Shared Flow 7 | GetSharedFlow 8 | 1634053049817 9 | jaspecla@microsoft.com 10 | 11 | Get-Shared-Flow 12 | 13 | 14 | 15 | SharedFlow 16 | 17 | default 18 | 19 | 20 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TestBundles/GetSharedFlow/sharedflowbundle/policies/Get-Shared-Flow.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | Get Shared Flow 4 | 3600 5 | environment 6 | 7 | 8 | MyKey 9 | 10 | 11 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TestBundles/GetSharedFlow/sharedflowbundle/sharedflows/default.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Get-Shared-Flow 5 | 6 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TestBundles/Test-API/apiproxy/Test-API.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | /profile 4 | 5 | 1554751795786 6 | jaspecla@microsoft.com 7 | Test API 8 | Test-API 9 | 1645821267509 10 | jaspecla@microsoft.com 11 | 12 | SharedFlowCallout 13 | 14 | 15 | default 16 | 17 | 18 | 19 | 20 | 21 | default 22 | 23 | 24 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TestBundles/Test-API/apiproxy/policies/SharedFlowCallout.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | SahredFlowCallout 4 | 5 | 6 | GetSharedFlow 7 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TestBundles/Test-API/apiproxy/proxies/default.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | /profile 15 | 16 | 17 | 18 | default 19 | 20 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TestBundles/Test-API/apiproxy/targets/default.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | SharedFlowCallout 9 | 10 | 11 | 12 | 13 | 14 | 15 | 180000 16 | 17 | https://SetInDynamicUrlSharedflow 18 | 19 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/TransformationTests/AssignMessageTransformationTests.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Service; 2 | using ApigeeToAzureApimMigrationTool.Service.Transformations; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToApimMigrationTool.Test.TransformationTests 11 | { 12 | public class AssignMessageTransformationTests 13 | { 14 | [Fact] 15 | public async Task Transform_AddHeaderPolicy_AddsHeader() 16 | { 17 | // Arrange 18 | var apigeePolicyElement = XElement.Parse( 19 | @" 20 | 21 | 22 |
value-1
23 |
24 |
25 |
"); 26 | 27 | var apigeePolicyName = "Add-Header-1"; 28 | 29 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 30 | 31 | // Act 32 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 33 | 34 | // Assert 35 | Assert.Single(result); 36 | Assert.Equal("set-header", result.First().Name.LocalName); 37 | Assert.Equal("header-1", result.First().Attribute("name").Value); 38 | Assert.Equal("value-1", result.First().Element("value").Value); 39 | } 40 | 41 | [Fact] 42 | public async Task Transform_SetHeaderPolicy_AddsHeader() 43 | { 44 | // Arrange 45 | var apigeePolicyElement = XElement.Parse( 46 | @" 47 | 48 | 49 |
value-1
50 |
51 |
52 |
"); 53 | 54 | var apigeePolicyName = "Set-Header-1"; 55 | 56 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 57 | 58 | // Act 59 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 60 | 61 | // Assert 62 | Assert.Single(result); 63 | Assert.Equal("set-header", result.First().Name.LocalName); 64 | Assert.Equal("override", result.First().Attribute("exists-action").Value); 65 | Assert.Equal("header-1", result.First().Attribute("name").Value); 66 | Assert.Equal("value-1", result.First().Element("value").Value); 67 | } 68 | 69 | [Fact] 70 | public async Task Transform_RemoveHeaderPolicy_RemovesHeader() 71 | { 72 | // Arrange 73 | var apigeePolicyElement = XElement.Parse( 74 | @" 75 | 76 | 77 |
78 | 79 | 80 | "); 81 | 82 | var apigeePolicyName = "Remove-Header-1"; 83 | 84 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 85 | 86 | // Act 87 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 88 | 89 | // Assert 90 | Assert.Single(result); 91 | Assert.Equal("set-header", result.First().Name.LocalName); 92 | Assert.Equal("delete", result.First().Attribute("exists-action").Value); 93 | Assert.Equal("header-1", result.First().Attribute("name").Value); 94 | } 95 | 96 | [Fact] 97 | public async Task Transform_SetPayloadPolicyWithJson_SetsJsonBody() 98 | { 99 | // Arrange 100 | var apigeePolicyElement = XElement.Parse( 101 | @" 102 | 103 | 104 | {""key"":""value""} 105 | 106 | 107 | "); 108 | 109 | var apigeePolicyName = "Set-Body-1"; 110 | 111 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 112 | 113 | // Act 114 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 115 | 116 | // Assert 117 | Assert.Single(result); 118 | Assert.Equal("set-body", result.First().Name.LocalName); 119 | Assert.Equal("{\"key\":\"value\"}", result.First().Value); 120 | } 121 | 122 | [Fact] 123 | public async Task Transform_AssignVariable_SetsVariable() 124 | { 125 | // Arrange 126 | var apigeePolicyElement = XElement.Parse( 127 | @" 128 | 129 | variable-1 130 | value-1 131 | 132 | "); 133 | 134 | var apigeePolicyName = "Assign-Variable-1"; 135 | 136 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 137 | 138 | // Act 139 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 140 | 141 | // Assert 142 | Assert.Single(result); 143 | Assert.Equal("set-variable", result.First().Name.LocalName); 144 | Assert.Equal("variable-1", result.First().Attribute("name").Value); 145 | Assert.Equal("value-1", result.First().Attribute("value").Value); 146 | } 147 | 148 | [Fact] 149 | public async Task Transform_AssignVariableWithTemplate_SetsTemplatedVariable() 150 | { 151 | // Arrange 152 | var apigeePolicyElement = XElement.Parse( 153 | @" 154 | 155 | variable-1 156 | 157 | 158 | "); 159 | 160 | var apigeePolicyName = "Assign-Variable-1"; 161 | 162 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 163 | 164 | // Act 165 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 166 | 167 | // Assert 168 | Assert.Single(result); 169 | Assert.Equal("set-variable", result.First().Name.LocalName); 170 | Assert.Equal("variable-1", result.First().Attribute("name").Value); 171 | Assert.Equal("@(context.Operation.Method)", result.First().Attribute("value").Value); 172 | 173 | } 174 | 175 | [Fact] 176 | public async Task Transform_AssignVariableWithComplexTemplate_SetsTemplatedVariable() 177 | { 178 | // Arrange 179 | var apigeePolicyElement = XElement.Parse( 180 | @" 181 | 182 | variable-1 183 | 184 | 185 | "); 186 | 187 | var apigeePolicyName = "Assign-Variable-1"; 188 | 189 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 190 | 191 | // Act 192 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 193 | 194 | // Assert 195 | Assert.Single(result); 196 | Assert.Equal("set-variable", result.First().Name.LocalName); 197 | Assert.Equal("variable-1", result.First().Attribute("name").Value); 198 | Assert.Equal("@(context.Operation.Method + \"/\" + context.Request.Headers.GetValueOrDefault(\"origin\"))", result.First().Attribute("value").Value); 199 | 200 | } 201 | 202 | [Fact] 203 | public async Task Transform_AssignVariableWithRefAndValue_SetsRefValueOrDefault() 204 | { 205 | // Arrange 206 | var apigeePolicyElement = XElement.Parse( 207 | @" 208 | 209 | variable-1 210 | my-default-value 211 | request.verb 212 | 213 | "); 214 | 215 | var apigeePolicyName = "Assign-Variable-1"; 216 | 217 | var sut = new AssignMessageTransformation(new ExpressionTranslator()); 218 | 219 | // Act 220 | var result = await sut.Transform(apigeePolicyElement, apigeePolicyName); 221 | 222 | // Assert 223 | Assert.Single(result); 224 | Assert.Equal("set-variable", result.First().Name.LocalName); 225 | Assert.Equal("variable-1", result.First().Attribute("name").Value); 226 | Assert.Equal("@(context.Operation.Method ? context.Operation.Method : \"my-default-value\")", result.First().Attribute("value").Value); 227 | 228 | } 229 | 230 | 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.Test/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Xunit; -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33516.290 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApigeeToApimMigrationTool", "ApigeeToApimMigrationTool\ApigeeToApimMigrationTool.csproj", "{A05BB193-76E0-4850-A369-EAA8F223BB3F}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApigeeToAzureApimMigrationTool.Service", "ApigeeToAzureApimMigrationTool.Logic\ApigeeToAzureApimMigrationTool.Service.csproj", "{7892713C-5601-475B-AEB8-D36EF3E313F3}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApigeeToAzureApimMigrationTool.Core", "ApigeeToAzureApimMigrationTool.Core\ApigeeToAzureApimMigrationTool.Core.csproj", "{91FF11ED-58E1-4021-AE1E-BDFA552C550B}" 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApigeeToApimMigrationTool.Test", "ApigeeToApimMigrationTool.Test\ApigeeToApimMigrationTool.Test.csproj", "{47FC9070-F20E-450A-9567-62753B34BFDC}" 13 | EndProject 14 | Project("{00D1A9C2-B5F0-4AF3-8072-F6C62B433612}") = "ApigeeToApimMigrationTool.Database", "ApigeeToApimMigrationTool.Database\ApigeeToApimMigrationTool.Database.sqlproj", "{0FB5C73E-88DD-44DE-9DD5-882D6DE57189}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ApigeeToApimMigrationTool.DataAccess", "ApigeeToApimMigrationTool.DataAccess\ApigeeToApimMigrationTool.DataAccess.csproj", "{871A0F9B-294C-4DAA-8FE1-7B2E37D14DFA}" 17 | EndProject 18 | Global 19 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 20 | Debug|Any CPU = Debug|Any CPU 21 | Release|Any CPU = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {A05BB193-76E0-4850-A369-EAA8F223BB3F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {A05BB193-76E0-4850-A369-EAA8F223BB3F}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {A05BB193-76E0-4850-A369-EAA8F223BB3F}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {A05BB193-76E0-4850-A369-EAA8F223BB3F}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {7892713C-5601-475B-AEB8-D36EF3E313F3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {7892713C-5601-475B-AEB8-D36EF3E313F3}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {7892713C-5601-475B-AEB8-D36EF3E313F3}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {7892713C-5601-475B-AEB8-D36EF3E313F3}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {91FF11ED-58E1-4021-AE1E-BDFA552C550B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {91FF11ED-58E1-4021-AE1E-BDFA552C550B}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {91FF11ED-58E1-4021-AE1E-BDFA552C550B}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {91FF11ED-58E1-4021-AE1E-BDFA552C550B}.Release|Any CPU.Build.0 = Release|Any CPU 36 | {47FC9070-F20E-450A-9567-62753B34BFDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 37 | {47FC9070-F20E-450A-9567-62753B34BFDC}.Debug|Any CPU.Build.0 = Debug|Any CPU 38 | {47FC9070-F20E-450A-9567-62753B34BFDC}.Release|Any CPU.ActiveCfg = Release|Any CPU 39 | {47FC9070-F20E-450A-9567-62753B34BFDC}.Release|Any CPU.Build.0 = Release|Any CPU 40 | {0FB5C73E-88DD-44DE-9DD5-882D6DE57189}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 41 | {0FB5C73E-88DD-44DE-9DD5-882D6DE57189}.Debug|Any CPU.Build.0 = Debug|Any CPU 42 | {0FB5C73E-88DD-44DE-9DD5-882D6DE57189}.Debug|Any CPU.Deploy.0 = Debug|Any CPU 43 | {0FB5C73E-88DD-44DE-9DD5-882D6DE57189}.Release|Any CPU.ActiveCfg = Release|Any CPU 44 | {0FB5C73E-88DD-44DE-9DD5-882D6DE57189}.Release|Any CPU.Build.0 = Release|Any CPU 45 | {0FB5C73E-88DD-44DE-9DD5-882D6DE57189}.Release|Any CPU.Deploy.0 = Release|Any CPU 46 | {871A0F9B-294C-4DAA-8FE1-7B2E37D14DFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {871A0F9B-294C-4DAA-8FE1-7B2E37D14DFA}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {871A0F9B-294C-4DAA-8FE1-7B2E37D14DFA}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {871A0F9B-294C-4DAA-8FE1-7B2E37D14DFA}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(ExtensibilityGlobals) = postSolution 55 | SolutionGuid = {503DB0F7-38A4-4604-B78B-4C3ABA9C857D} 56 | EndGlobalSection 57 | EndGlobal 58 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool/ApigeeToApimMigrationTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net7.0 6 | enable 7 | enable 8 | 9 | false 10 | true 11 | 12 | 13 | win10-x64;linux-x64 14 | 15 | AnyCPU 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | True 38 | True 39 | Resources.resx 40 | 41 | 42 | 43 | 44 | 45 | ResXFileCodeGenerator 46 | Resources.Designer.cs 47 | 48 | 49 | 50 | 51 | 52 | Always 53 | 54 | 55 | Always 56 | 57 | 58 | Always 59 | 60 | 61 | Always 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ApigeeToApimMigrationTool": { 4 | "commandName": "Project", 5 | "commandLineArgs": "--configFile apigeeToApimConfig.json" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /ApigeeToApimMigrationTool/apigeeToApimConfig.SAMPLE.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DataConnection": "" 4 | }, 5 | "Apigee": { 6 | "OrganizationName": "my apigee org", 7 | "AuthenticationBaseUrl": "https://yourcompany.login.apigee.com", 8 | "ManagementBaseUrl": "https://api.enterprise.apigee.com", 9 | "Passcode": "my passcode", 10 | "Username": "my username", 11 | "Password": "my password", 12 | "ProxyOrProduct": "Product", 13 | "ProxyOrProductName": "MyProductName", 14 | "EnvironmentName": "my environment", 15 | "ConfigDir": "C:\\apigee\\files\\MyFiles", 16 | "UseTestService": false 17 | }, 18 | "Entra": { 19 | "AppId": "my app ID", 20 | "Password": "my app secret", 21 | "TenantId": "my tenant ID", 22 | "SubscriptionId": "my subscription ID" 23 | }, 24 | "Apim": { 25 | "Url": "https://my-apim.azure-api.net", 26 | "Name": "my-apim-name", 27 | "ResourceGroup": "My_Apim_Resource_Group", 28 | "OAuthConfigName": "my-apim-oauth-config-name", 29 | "OAuthBackendAppId": "my-apim-oauth-backend-app-id", 30 | "OAuthTenantId": "my-apim-oauth-tenant-id" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/ApigeeToAzureApimMigrationTool.Core.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Config/ApigeeConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToApimMigrationTool.Core.Config 8 | { 9 | public class ApigeeConfiguration 10 | { 11 | public string? OrganizationName { get; set; } 12 | public string? AuthenticationBaseUrl { get; set; } 13 | public string? ManagementBaseUrl { get; set; } 14 | public string? Passcode { get; set; } 15 | public string? Username { get; set; } 16 | public string? Password { get; set; } 17 | public string? ProxyOrProduct { get; set; } 18 | public string? ProxyOrProductName { get; set; } 19 | public string? EnvironmentName { get; set; } 20 | public string? ConfigDir { get; set; } 21 | public bool UseTestService { get; set; } 22 | public bool UseApigeeDisplayName { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Config/ApigeeConfigurationBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.CommandLine; 4 | using System.CommandLine.Binding; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToApimMigrationTool.Core.Config 10 | { 11 | public class ApigeeConfigurationBinder : BinderBase 12 | { 13 | private readonly Option _organizationNameOption; 14 | private readonly Option _authenticationBaseUrlOption; 15 | private readonly Option _managementBaseUrlOption; 16 | private readonly Option _passcodeOption; 17 | private readonly Option _usernameOption; 18 | private readonly Option _passwordOption; 19 | private readonly Option _proxyOrProductOption; 20 | private readonly Option _proxyOrProductNameOption; 21 | private readonly Option _environmentNameOption; 22 | private readonly Option _configDirOption; 23 | private readonly Option _useApigeeDisplayName; 24 | 25 | public ApigeeConfigurationBinder( 26 | Option organizationNameOption, 27 | Option authenticationBaseUrlOption, 28 | Option managementBaseUrlOption, 29 | Option passcodeOption, 30 | Option usernameOption, 31 | Option passwordOption, 32 | Option proxyOrProductOption, 33 | Option proxyOrProductNameOption, 34 | Option environmentNameOption, 35 | Option configDirOption, 36 | Option useApigeeDisplayName) 37 | { 38 | _organizationNameOption = organizationNameOption; 39 | _authenticationBaseUrlOption = authenticationBaseUrlOption; 40 | _managementBaseUrlOption = managementBaseUrlOption; 41 | _passcodeOption = passcodeOption; 42 | _usernameOption = usernameOption; 43 | _passwordOption = passwordOption; 44 | _proxyOrProductOption = proxyOrProductOption; 45 | _proxyOrProductNameOption = proxyOrProductNameOption; 46 | _environmentNameOption = environmentNameOption; 47 | _configDirOption = configDirOption; 48 | _useApigeeDisplayName = useApigeeDisplayName; 49 | } 50 | 51 | protected override ApigeeConfiguration GetBoundValue(BindingContext bindingContext) => 52 | new ApigeeConfiguration 53 | { 54 | OrganizationName = bindingContext.ParseResult.GetValueForOption(_organizationNameOption), 55 | AuthenticationBaseUrl = bindingContext.ParseResult.GetValueForOption(_authenticationBaseUrlOption), 56 | ManagementBaseUrl = bindingContext.ParseResult.GetValueForOption(_managementBaseUrlOption), 57 | Passcode = bindingContext.ParseResult.GetValueForOption(_passcodeOption), 58 | Username = bindingContext.ParseResult.GetValueForOption(_usernameOption), 59 | Password = bindingContext.ParseResult.GetValueForOption(_passwordOption), 60 | ProxyOrProduct = bindingContext.ParseResult.GetValueForOption(_proxyOrProductOption), 61 | ProxyOrProductName = bindingContext.ParseResult.GetValueForOption(_proxyOrProductNameOption), 62 | EnvironmentName = bindingContext.ParseResult.GetValueForOption(_environmentNameOption), 63 | ConfigDir = bindingContext.ParseResult.GetValueForOption(_configDirOption), 64 | UseApigeeDisplayName = bindingContext.ParseResult.GetValueForOption(_useApigeeDisplayName) 65 | }; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Config/ApimConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToApimMigrationTool.Core.Config 8 | { 9 | public class ApimConfiguration 10 | { 11 | public string? Url { get; set; } 12 | public string? Name { get; set; } 13 | public string? ResourceGroup { get; set; } 14 | public string? OAuthConfigName { get; set; } 15 | public string? OAuthBackendAppId { get; set; } 16 | public string? OAuthTenantId { get; set; } 17 | public string? OAuthAudiences { get; set; } 18 | public string? OAuthIssuers { get; set; } 19 | public string? OAuthScopeClaimName { get; set; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Config/ApimConfigurationBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.CommandLine; 4 | using System.CommandLine.Binding; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToApimMigrationTool.Core.Config 10 | { 11 | public class ApimConfigurationBinder : BinderBase 12 | { 13 | private readonly Option _urlOption; 14 | private readonly Option _nameOption; 15 | private readonly Option _resourceGroupOption; 16 | private readonly Option _oAuthConfigNameOption; 17 | private readonly Option _oAuthBackendAppIdOption; 18 | private readonly Option _oAuthTenantIdOption; 19 | private readonly Option _oAuthAudienceOption; 20 | private readonly Option _oAuthIssuersOptionn; 21 | private readonly Option _oAuthScopeClaimNameOptionn; 22 | 23 | public ApimConfigurationBinder( 24 | Option urlOption, 25 | Option nameOption, 26 | Option resourceGroupOption, 27 | Option oAuthConfigNameOption, 28 | Option oAuthBackendAppIdOption, 29 | Option oAuthTenantIdOption, 30 | Option oAuthAudienceOption, 31 | Option oAuthIssuersOptionn, 32 | Option oAuthScopeClaimNameOptionn) 33 | { 34 | _nameOption = nameOption; 35 | _resourceGroupOption = resourceGroupOption; 36 | _oAuthConfigNameOption = oAuthConfigNameOption; 37 | _oAuthBackendAppIdOption = oAuthBackendAppIdOption; 38 | _oAuthTenantIdOption = oAuthTenantIdOption; 39 | _oAuthAudienceOption = oAuthAudienceOption; 40 | _oAuthIssuersOptionn = oAuthIssuersOptionn; 41 | _oAuthScopeClaimNameOptionn = oAuthScopeClaimNameOptionn; 42 | _urlOption = urlOption; 43 | } 44 | 45 | protected override ApimConfiguration GetBoundValue(BindingContext bindingContext) => new ApimConfiguration 46 | { 47 | Url = bindingContext.ParseResult.GetValueForOption(_urlOption), 48 | Name = bindingContext.ParseResult.GetValueForOption(_nameOption), 49 | ResourceGroup = bindingContext.ParseResult.GetValueForOption(_resourceGroupOption), 50 | OAuthConfigName = bindingContext.ParseResult.GetValueForOption(_oAuthConfigNameOption), 51 | OAuthBackendAppId = bindingContext.ParseResult.GetValueForOption(_oAuthBackendAppIdOption), 52 | OAuthTenantId = bindingContext.ParseResult.GetValueForOption(_oAuthTenantIdOption), 53 | OAuthAudiences = bindingContext.ParseResult.GetValueForOption(_oAuthAudienceOption), 54 | OAuthIssuers = bindingContext.ParseResult.GetValueForOption(_oAuthIssuersOptionn), 55 | OAuthScopeClaimName = bindingContext.ParseResult.GetValueForOption(_oAuthScopeClaimNameOptionn) 56 | }; 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Config/EntraConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToApimMigrationTool.Core.Config 8 | { 9 | public class EntraConfiguration 10 | { 11 | public string? AppId { get; set; } 12 | public string? Password { get; set; } 13 | public string? TenantId { get; set; } 14 | public string? SubscriptionId { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Config/EntraConfigurationBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.CommandLine; 4 | using System.CommandLine.Binding; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToApimMigrationTool.Core.Config 10 | { 11 | public class EntraConfigurationBinder : BinderBase 12 | { 13 | private readonly Option _appIdOption; 14 | private readonly Option _passwordOption; 15 | private readonly Option _tenantIdOption; 16 | private readonly Option _subscriptionIdOption; 17 | 18 | public EntraConfigurationBinder( 19 | Option appIdOption, 20 | Option passwordOption, 21 | Option tenantIdOption, 22 | Option subscriptionIdOption) 23 | { 24 | _appIdOption = appIdOption; 25 | _passwordOption = passwordOption; 26 | _tenantIdOption = tenantIdOption; 27 | _subscriptionIdOption = subscriptionIdOption; 28 | } 29 | 30 | protected override EntraConfiguration GetBoundValue(BindingContext bindingContext) => 31 | new EntraConfiguration 32 | { 33 | AppId = bindingContext.ParseResult.GetValueForOption(_appIdOption), 34 | Password = bindingContext.ParseResult.GetValueForOption(_passwordOption), 35 | TenantId = bindingContext.ParseResult.GetValueForOption(_tenantIdOption), 36 | SubscriptionId = bindingContext.ParseResult.GetValueForOption(_subscriptionIdOption) 37 | }; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/ApiProductMetaData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.dto 8 | { 9 | public class ApiProductMetaData 10 | { 11 | public string DisplayName { get; set; } 12 | public string Description { get; set; } 13 | public string Name { get; set; } 14 | public IList Proxies { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/ApiProxyEndpointMetadata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.dto 8 | { 9 | public class ApiProxyEndpointMetadata 10 | { 11 | public ApiProxyEndpointConnection Connection { get; set; } 12 | } 13 | 14 | public class ApiProxyEndpointConnection 15 | { 16 | public string BasePath { get; set; } 17 | public string ConnectionType { get; set; } 18 | public string[] VirtualHost { get; set; } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/ApiProxyMetaData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Dto 8 | { 9 | public class ApiProxyMetaData 10 | { 11 | public long createdAt { get; set; } 12 | public string createdBy { get; set; } 13 | public long lastModifiedAt { get; set; } 14 | public string lastModifiedBy { get; set; } 15 | public string subType { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/ApiProxyRevisionMetadata.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Dto 8 | { 9 | public class ApiProxyRevisionMetadata 10 | { 11 | public int Id { get; set; } 12 | public string[] BasePaths { get; set; } 13 | public string Name { get; set; } 14 | public int Revision { get; set; } 15 | public string Description { get; set; } 16 | public string[] Policies { get; set; } 17 | public string[] ProxyEndpoints { get; set; } 18 | public Dictionary ProxyEndpointPaths { get; set; } 19 | 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/ApigeeEntityModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Cryptography.X509Certificates; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ApigeeToAzureApimMigrationTool.Core.Dto 9 | { 10 | public class ApigeeEntityModel 11 | { 12 | public string name { get; set; } 13 | public string[] revision { get; set; } 14 | public ApiProxyMetaData metaData { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/ApigeeTargetServerModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Dto 8 | { 9 | public class ApigeeTargetServerModel 10 | { 11 | public string Host { get; set; } 12 | public bool IsEnabled { get; set; } 13 | public string Name { get; set; } 14 | public int Port { get; set; } 15 | public ApigeeTargetServerSSLInfo SSLInfo { get; set; } 16 | } 17 | 18 | public class ApigeeTargetServerSSLInfo 19 | { 20 | public ICollection Ciphers { get; set; } 21 | public bool ClientAuthEnabled { get; set; } 22 | public bool Enabled { get; set; } 23 | public bool IgnoreValidationError { get; set; } 24 | public ICollection Protocols { get; set; } 25 | public string TrustStore { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/ApimConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.dto 8 | { 9 | public class ApimConfig 10 | { 11 | public string ApimUrl { get; set; } 12 | public string ApimName { get; set; } 13 | public string ApimResourceGroupName { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/AuthToken.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Dto 8 | { 9 | public class AuthToken 10 | { 11 | public string access_token { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/AzureCredentials.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Dto 8 | { 9 | public class AzureCredentials 10 | { 11 | public Guid AppId { get; set; } 12 | public string DisplayName { get; set; } 13 | public string Password { get; set; } 14 | public Guid Tenant { get; set; } 15 | public string SubscriptionId { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Dto/KeyValueMapModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.dto 8 | { 9 | public class KeyValueMapModel 10 | { 11 | public bool Encrypted { get; set; } 12 | public string Name { get; set; } 13 | public ICollection Entry { get; set; } 14 | } 15 | 16 | public class KeyValueMapItemModel 17 | { 18 | public string Name { get; set; } 19 | public string Value { get; set; } 20 | } 21 | } 22 | 23 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Enum/AssignMessagePolicyOperations.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ApigeeToAzureApimMigrationTool.Core.Enum 9 | { 10 | public enum AssignMessagePolicyOperations 11 | { 12 | Add, 13 | Remove, 14 | Set 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Enum/PolicyDirection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Enum 8 | { 9 | public enum PolicyDirection 10 | { 11 | Inbound, 12 | Outbound 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IApigeeManagementApiService.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.dto; 2 | using ApigeeToAzureApimMigrationTool.Core.Dto; 3 | 4 | namespace ApigeeToAzureApimMigrationTool.Core 5 | { 6 | public interface IApigeeManagementApiService 7 | { 8 | string AuthenticationToken { get; set; } 9 | string Username { get; set; } 10 | string Password { get; set; } 11 | string? Environment { get; } 12 | string ProxyName { get; } 13 | Task DownloadApiProxyBundle(string basePath, string proxyName, int revision); 14 | Task GetApiProxyByName(string proxyName); 15 | Task DownloadSharedFlowBundle(string basePath, string sharedFlowName, int revision); 16 | Task GetSharedFlowByName(string sharedFlowName); 17 | Task PopulateProxyReferenceDatabase(); 18 | Task GetApiProductByName(string productName); 19 | Task GetTargetServerByName(string targetServerName, string environment); 20 | Task GetKeyValueMapByName(string proxyName, string environment, string mapIdentifier); 21 | } 22 | } -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IApigeeXmlLoader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using System.Xml.Linq; 7 | 8 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 9 | { 10 | public interface IApigeeXmlLoader 11 | { 12 | public XDocument LoadProxyXml(string proxyName); 13 | 14 | public XDocument LoadTargetXml(string proxyName, string targetEndpointName); 15 | 16 | public XDocument LoadProxyEndpointXml(string proxyName, string proxyEndpointName); 17 | public XDocument LoadPolicyXml(string proxyName, string policyName); 18 | public XDocument LoadSharedFlowBundleXml(string sharedFlowName); 19 | public XDocument LoadSharedFlowXml(string sharedFlowName, string sharedFlowFileName); 20 | public XDocument LoadSharedFlowPolicyXml(string sharedFlowName, string policyName); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IApimPolicyTransformer.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Linq; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 10 | { 11 | public interface IApimPolicyTransformer 12 | { 13 | Task TransformPoliciesInCollection(IEnumerable? elements, XElement azureApimPolicySection, Func xmlLoader, 14 | string apimName, string proxyName, ApigeeConfiguration apigeeConfiguration, ApimConfiguration apimConfig); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IApimProvider.cs: -------------------------------------------------------------------------------- 1 | using Azure.ResourceManager.ApiManagement; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Linq; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 10 | { 11 | public interface IApimProvider 12 | { 13 | string ApimName { get; } 14 | string ApimUrl { get; } 15 | Task CreateApi(string apiName, string apiDisplayName, string apiDescription, string apimName, 16 | string revision, string apiPath, string backendUrl, string? oauthConfigurationName); 17 | Task CreateProduct(string name, string displayName, string description, string apimName); 18 | Task CreatePolicyFragment(string policyFragmentName, string apimName, string policyFragmentXml, string policyFragmentDescription); 19 | Task CreatePolicy(XDocument policyXml); 20 | Task CreateOrUpdateOperation(string apiName, string description, string httpVerb); 21 | Task CreateOrUpdateOperationPolicy(XDocument operationPolicyXml, string operationName, string operationDescription, string httpVerb, string proxyPath); 22 | Task AddApiToProduct(string apiId); 23 | Task AddNamedValue(string apimName, string proxyName, string mapIdentifier, string keyName, bool isSecret, string value, int index = 1); 24 | Task UpdateApiSubscriptionSetting(string apimName, string proxyName, string headerName = "", string queryParameterName = ""); 25 | 26 | string RemoveTrailingSpecialCharacters(string input); 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IAzureApimService.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using Azure.ResourceManager.ApiManagement; 3 | 4 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 5 | { 6 | public interface IAzureApimService 7 | { 8 | Task ImportApi(string apimName, string proxyName, ApimConfiguration apimConfiguration, ApigeeConfiguration apigeeConfiguration, string keyVaultName); 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IBundle.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 8 | { 9 | public interface IBundle 10 | { 11 | Task LoadBundle(); 12 | string GetBundlePath(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IBundleProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 8 | { 9 | public interface IBundleProvider 10 | { 11 | IBundle GetApiProxyBundle(string proxyOrProductName); 12 | IBundle GetSharedFlowBundle(string sharedFlowName); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IExpressionTranslator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 8 | { 9 | public interface IExpressionTranslator 10 | { 11 | bool ContentHasVariablesInIt(string content); 12 | string TranslateSingleItem(string expression, string defaultValue = ""); 13 | string TranslateWholeString(string expression); 14 | string TranslateConditionOperator(string expression); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IPolicyTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Linq; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 10 | { 11 | public interface IPolicyTransformation 12 | { 13 | Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IPolicyTransformationFactory.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ApigeeToAzureApimMigrationTool.Core.Interface 9 | { 10 | public interface IPolicyTransformationFactory 11 | { 12 | IPolicyTransformation GetTransformationForPolicy(string policyName, IList> policyVariables, ApigeeConfiguration apigeeConfiguration, ApimConfiguration apimConfig); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Core/Interface/IProxyMetaDataDataAccess.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Dto; 2 | 3 | namespace ApigeeToApimMigrationTool.Core.Interface 4 | { 5 | public interface IProxyMetaDataDataAccess 6 | { 7 | Task GetProxyRevisionMetaDataByPath(string path); 8 | Task InsertProxyRevisionMetaData(ApiProxyRevisionMetadata metaData); 9 | Task IsProxyMetadataTablePopulted(); 10 | } 11 | } -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/ApigeeManagementApiTestFileService.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.dto; 3 | using ApigeeToAzureApimMigrationTool.Core.Dto; 4 | using ApigeeToAzureApimMigrationTool.Core.Interface; 5 | using ApigeeToAzureApimMigrationTool.Service.Bundles; 6 | using Newtonsoft.Json; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Linq; 10 | using System.Text; 11 | using System.Threading.Tasks; 12 | using System.Xml.Linq; 13 | 14 | namespace ApigeeToAzureApimMigrationTool.Service 15 | { 16 | public class ApigeeManagementApiTestFileService : IApigeeManagementApiService 17 | { 18 | private readonly IBundleProvider _apigeeBundleProvider; 19 | private readonly IApigeeXmlLoader _apigeeXmlLoader; 20 | private readonly string _localConfigPath; 21 | public ApigeeManagementApiTestFileService(IBundleProvider apigeeBundleProvider, IApigeeXmlLoader apigeeXmlLoader, string localConfigPath) 22 | { 23 | _apigeeBundleProvider = apigeeBundleProvider; 24 | _apigeeXmlLoader = apigeeXmlLoader; 25 | _localConfigPath = localConfigPath; 26 | } 27 | public string AuthenticationToken { get; set; } 28 | public string Username { get; set; } 29 | public string Password { get; set; } 30 | 31 | public string? Environment { get; set; } 32 | 33 | public string ProxyName { get; set; } 34 | 35 | public Task GetApiProxyByName(string proxyName) 36 | { 37 | // For testing, do things backwards. Get the metadata from the XML that we've 38 | // already downloaded. 39 | XDocument proxyXml = _apigeeXmlLoader.LoadProxyXml(proxyName); 40 | if (proxyXml.Root == null) 41 | { 42 | throw new Exception($"Could not get xml for proxy {proxyName} from file."); 43 | } 44 | 45 | XElement? apiProxyElement = proxyXml.Root.Element("APIProxy"); 46 | if (apiProxyElement == null) 47 | { 48 | throw new Exception($"Could not find APIProxy element in xml for proxy {proxyName}."); 49 | } 50 | 51 | var apiProxyModel = new ApigeeEntityModel(); 52 | apiProxyModel.name = apiProxyElement.Attribute("name")?.Value ?? string.Empty; 53 | 54 | string revision = apiProxyElement.Attribute("revision")?.Value ?? string.Empty; 55 | apiProxyModel.revision = new string[] { revision }; 56 | 57 | apiProxyModel.metaData = new ApiProxyMetaData(); 58 | apiProxyModel.metaData.createdAt = long.Parse(apiProxyElement.Element("CreatedAt")?.Value ?? string.Empty); 59 | apiProxyModel.metaData.createdBy = apiProxyElement.Element("CreatedBy")?.Value ?? string.Empty; 60 | apiProxyModel.metaData.lastModifiedAt = long.Parse(apiProxyElement.Element("LastModifiedAt")?.Value ?? string.Empty); 61 | apiProxyModel.metaData.lastModifiedBy = apiProxyElement.Element("LastModifiedBy")?.Value ?? string.Empty; 62 | 63 | apiProxyModel.metaData.subType = string.Empty; 64 | 65 | return Task.FromResult(apiProxyModel); 66 | } 67 | 68 | public Task GetTargetServerByName(string targetServerName, string environment) 69 | { 70 | var defaultTargetServer = new ApigeeTargetServerModel 71 | { 72 | Host = targetServerName, 73 | IsEnabled = true, 74 | Name = targetServerName, 75 | Port = 8080, 76 | SSLInfo = new ApigeeTargetServerSSLInfo 77 | { 78 | Ciphers = new string[] { "DEFAULT" }, 79 | ClientAuthEnabled = false, 80 | Enabled = false, 81 | IgnoreValidationError = false, 82 | Protocols = new string[] { "TLSv1.2" }, 83 | TrustStore = "default" 84 | } 85 | }; 86 | 87 | return Task.FromResult(defaultTargetServer); 88 | } 89 | 90 | public Task DownloadApiProxyBundle(string basePath, string proxyName, int revision) 91 | { 92 | // Return the path of the already downloaded bundle 93 | var bundle = _apigeeBundleProvider.GetApiProxyBundle(proxyName); 94 | return Task.FromResult(bundle.GetBundlePath()); 95 | } 96 | 97 | public Task DownloadSharedFlowBundle(string basePath, string sharedFlowName, int revision) 98 | { 99 | // Return the path of the already downloaded shared flow bundle 100 | var bundle = _apigeeBundleProvider.GetSharedFlowBundle(sharedFlowName); 101 | return Task.FromResult(bundle.GetBundlePath()); 102 | } 103 | 104 | public async Task GetApiProductByName(string productName) 105 | { 106 | var defaultApiProductMetaData = new ApiProductMetaData 107 | { 108 | DisplayName = productName, 109 | Description = "Default", 110 | Name = productName, 111 | Proxies = new List(), 112 | }; 113 | 114 | // Iterate through the bundle directory and find all the proxies that are in the product 115 | var bundleDirectories = Directory.GetDirectories(_localConfigPath); 116 | foreach (var directory in bundleDirectories) 117 | { 118 | if (Directory.Exists($"{directory}{Path.DirectorySeparatorChar}apiproxy")) 119 | { 120 | defaultApiProductMetaData.Proxies.Add(Path.GetFileName(directory)); 121 | } 122 | } 123 | // END TODO 124 | 125 | // Load pre-configured product json file 126 | var apiProductFilePathBuilder = new StringBuilder($"{_localConfigPath}{Path.DirectorySeparatorChar}apiproducts{Path.DirectorySeparatorChar}{productName}.json"); 127 | 128 | string apiProductFilePath = apiProductFilePathBuilder.ToString(); 129 | 130 | if (!File.Exists(apiProductFilePath)) 131 | { 132 | return defaultApiProductMetaData; 133 | } 134 | 135 | string apiProductJson = await File.ReadAllTextAsync(apiProductFilePath); 136 | var apiProductModel = JsonConvert.DeserializeObject(apiProductJson); 137 | 138 | if (apiProductModel == null) 139 | { 140 | return defaultApiProductMetaData; 141 | } 142 | 143 | return apiProductModel; 144 | } 145 | 146 | 147 | public async Task GetKeyValueMapByName(string proxyName, string environment, string mapIdentifier) 148 | { 149 | 150 | // Load pre-configured kvmap json file 151 | var keyValueMapFilePathBuilder = new StringBuilder($"{_localConfigPath}{Path.DirectorySeparatorChar}keyvaluemaps{Path.DirectorySeparatorChar}{mapIdentifier}"); 152 | if (!string.IsNullOrEmpty(environment)) 153 | { 154 | keyValueMapFilePathBuilder.Append($".{environment}"); 155 | } 156 | keyValueMapFilePathBuilder.Append(".json"); 157 | 158 | string keyValueMapPath = keyValueMapFilePathBuilder.ToString(); 159 | 160 | if (!File.Exists(keyValueMapPath)) 161 | { 162 | return null; // Same behavior as real service 163 | } 164 | 165 | string kvmapJson = await File.ReadAllTextAsync(keyValueMapPath); 166 | var kvmapModel = JsonConvert.DeserializeObject(kvmapJson); 167 | 168 | return kvmapModel; 169 | } 170 | 171 | public Task GetSharedFlowByName(string sharedFlowName) 172 | { 173 | // Just like with the API proxy metadata, do things backwards. Get the metadata from the XML that we've 174 | // already downloaded. 175 | // HACK: We're going to assume we've downlaoded the "default.xml" file for the shared flow. 176 | XDocument proxyXml = _apigeeXmlLoader.LoadSharedFlowBundleXml(sharedFlowName); 177 | if (proxyXml.Root == null) 178 | { 179 | throw new Exception($"Could not get xml for shared flow {sharedFlowName} from file."); 180 | } 181 | 182 | XElement? sharedFlowBundleElement = proxyXml.Element("SharedFlowBundle"); 183 | if (sharedFlowBundleElement == null) 184 | { 185 | throw new Exception($"Could not find SharedFlowBundle element in xml for shared flow {sharedFlowName}."); 186 | } 187 | 188 | var sharedFlowModel = new ApigeeEntityModel(); 189 | sharedFlowModel.name = sharedFlowBundleElement.Attribute("name")?.Value ?? string.Empty; 190 | 191 | string revision = sharedFlowBundleElement.Attribute("revision")?.Value ?? string.Empty; 192 | sharedFlowModel.revision = new string[] { revision }; 193 | 194 | sharedFlowModel.metaData = new ApiProxyMetaData(); 195 | sharedFlowModel.metaData.createdAt = long.Parse(sharedFlowBundleElement.Element("CreatedAt")?.Value ?? string.Empty); 196 | sharedFlowModel.metaData.createdBy = sharedFlowBundleElement.Element("CreatedBy")?.Value ?? string.Empty; 197 | sharedFlowModel.metaData.lastModifiedAt = long.Parse(sharedFlowBundleElement.Element("LastModifiedAt")?.Value ?? string.Empty); 198 | sharedFlowModel.metaData.lastModifiedBy = sharedFlowBundleElement.Element("LastModifiedBy")?.Value ?? string.Empty; 199 | 200 | sharedFlowModel.metaData.subType = string.Empty; 201 | 202 | return Task.FromResult(sharedFlowModel); 203 | } 204 | 205 | 206 | public Task PopulateProxyReferenceDatabase() 207 | { 208 | throw new NotImplementedException(); 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/ApigeeToApimPolicyTransformer.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using ApigeeToAzureApimMigrationTool.Service.Transformations; 4 | using Azure.ResourceManager.ApiManagement.Models; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Configuration; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Text; 11 | using System.Text.RegularExpressions; 12 | using System.Threading.Tasks; 13 | using System.Web; 14 | using System.Xml.Linq; 15 | 16 | namespace ApigeeToAzureApimMigrationTool.Service 17 | { 18 | public class ApigeeToApimPolicyTransformer : IApimPolicyTransformer 19 | { 20 | 21 | private readonly IPolicyTransformationFactory _policyTransformationFactory; 22 | private readonly List> _policyVariables; 23 | private readonly IApimProvider _apimProvider; 24 | private readonly IExpressionTranslator _expressionTranslator; 25 | 26 | public ApigeeToApimPolicyTransformer(IPolicyTransformationFactory policyTransformationFactory, IApimProvider apimProvider, IExpressionTranslator expressionTranslator) 27 | { 28 | _policyTransformationFactory = policyTransformationFactory; 29 | _policyVariables = new List>(); 30 | _apimProvider = apimProvider; 31 | _expressionTranslator = expressionTranslator; 32 | } 33 | public async Task TransformPoliciesInCollection(IEnumerable? elements, XElement azureApimPolicySection, Func xmlLoader, 34 | string apimName, string proxyName, ApigeeConfiguration apigeeConfiguration, ApimConfiguration apimConfig) 35 | { 36 | if (elements == null) 37 | { 38 | return; 39 | } 40 | 41 | foreach (var element in elements) 42 | { 43 | if (element == null) 44 | { 45 | continue; 46 | } 47 | 48 | string? policyName = element.Element("Name")?.Value; 49 | 50 | if (policyName == null) 51 | { 52 | throw new Exception($"Cannot find Name element in policy xml: {element}"); 53 | } 54 | 55 | XElement? conditionElement = element.Element("Condition"); 56 | string? condition = conditionElement?.Value; 57 | 58 | if (condition == null) 59 | { 60 | condition = string.Empty; 61 | } 62 | 63 | var policyXml = xmlLoader(proxyName, policyName); 64 | 65 | var rootElement = policyXml.Root; 66 | if (rootElement == null) 67 | { 68 | throw new Exception($"Cannot find root element in policy xml: {policyXml}"); 69 | } 70 | 71 | await TransformPolicy(proxyName, rootElement, rootElement.Name.ToString(), azureApimPolicySection, apimName, condition, policyName, apigeeConfiguration, apimConfig); 72 | } 73 | 74 | } 75 | 76 | private async Task TransformPolicy(string apiProxyName, XElement element, string apigeePolicyName, XElement apimPolicyElement, string apimName, string condition, string apigeePolicyDisplayName, 77 | ApigeeConfiguration apigeeConfiguration, ApimConfiguration apimConfig) 78 | { 79 | IPolicyTransformation? policyTransformation = default; 80 | if (apigeePolicyName.Equals("VerifyAPIKey")) 81 | { 82 | string headerValue = ""; 83 | string queryParameterValue = ""; 84 | var source = element.Element("APIKey").Attribute("ref").Value; 85 | if (source.StartsWith("request.header")) 86 | headerValue = source.Replace("request.header.", ""); 87 | else if (source.StartsWith("request.queryparam")) 88 | queryParameterValue = source.Replace("request.queryparam.", ""); 89 | 90 | await _apimProvider.UpdateApiSubscriptionSetting(apimConfig.Name, apiProxyName, headerValue, queryParameterValue); 91 | } 92 | else 93 | { 94 | policyTransformation = _policyTransformationFactory.GetTransformationForPolicy(apigeePolicyName, _policyVariables, apigeeConfiguration, apimConfig); 95 | var apimPolicies = await policyTransformation.Transform(element, apigeePolicyDisplayName); 96 | foreach (var apimPolicy in apimPolicies) 97 | { 98 | var policyWithCondition = ApplyConditionToPolicy(condition, apimPolicy); 99 | apimPolicyElement.Add(policyWithCondition); 100 | } 101 | } 102 | 103 | // Special handling for Shared Flows, for which additional policies need to be downloaded from Apigee 104 | // and then processed by this transformer entirely separately 105 | var flowCalloutTransformation = policyTransformation as FlowCalloutTransformation; 106 | if (flowCalloutTransformation != null) 107 | { 108 | await flowCalloutTransformation.DonwloadAndTransformSharedFlow(this); 109 | } 110 | } 111 | 112 | private XElement ApplyConditionToPolicy(string condition, XElement policy) 113 | { 114 | if (!string.IsNullOrEmpty(condition)) 115 | { 116 | 117 | condition = _expressionTranslator.TranslateWholeString(condition); 118 | 119 | string requestHeaderPattern = "request\\.header\\.([-\\w]+)"; 120 | foreach (Match match in Regex.Matches(condition, requestHeaderPattern)) 121 | { 122 | if (match.Success && match.Groups.Count > 0) 123 | { 124 | var HeaderName = match.Groups[1].Value; 125 | string completeExpression = $"request.header.{HeaderName}"; 126 | condition = condition.Replace(completeExpression, $"context.Request.Headers.GetValueOrDefault(\"{HeaderName}\", \"\")"); 127 | } 128 | } 129 | 130 | string cacheLookupPattern = @"lookupcache.(.*?).cachehit"; 131 | foreach (Match match in Regex.Matches(condition, cacheLookupPattern)) 132 | { 133 | if (match.Success && match.Groups.Count > 0) 134 | { 135 | var policyName = match.Groups[1].Value; 136 | string completeExpression = $"lookupcache.{policyName}.cachehit"; 137 | string variableName = _policyVariables.FirstOrDefault(x => x.Key == policyName).Value; 138 | condition = condition.Replace(completeExpression, $"context.Variables.ContainsKey(\"{variableName}\")"); 139 | } 140 | } 141 | 142 | string jwtValidPattern = @"jwt.(.*?).valid"; 143 | foreach (Match match in Regex.Matches(condition, jwtValidPattern)) 144 | { 145 | if (match.Success && match.Groups.Count > 0) 146 | { 147 | var policyName = match.Groups[1].Value; 148 | string completeExpression = $"jwt.{policyName}.valid"; 149 | string variableName = _policyVariables.FirstOrDefault(x => x.Key == policyName).Value; 150 | condition = condition.Replace(completeExpression, $"context.Variables.ContainsKey(\"{variableName}\")"); 151 | } 152 | } 153 | 154 | string variableEqualsValue = "(\\S+)\\s(=|==|!=|=\\\\|)\\s(\\S+)"; 155 | foreach (Match match in Regex.Matches(condition, variableEqualsValue)) 156 | { 157 | if (match.Success && match.Groups.Count > 0 && !match.Groups[0].Value.Contains("context.")) 158 | { 159 | var variableName = match.Groups[1].Value; 160 | var conditionOperator = match.Groups[2].Value; 161 | var variableValue = match.Groups[3].Value; 162 | if (!variableName.Contains("context") && !string.IsNullOrEmpty(variableName)) 163 | { 164 | string valueToReplace = match.Groups[0].Value; 165 | valueToReplace = valueToReplace.Replace("(", "").Replace(")", ""); 166 | condition = condition.Replace(valueToReplace, $"context.Variables.GetValueOrDefault<{GetDataTypeFromStringValue(variableValue)}>(\"{variableName.Replace("(", "").Replace(")", "")}\") {conditionOperator} {variableValue.Replace("(", "").Replace(")", "")}"); 167 | } 168 | } 169 | } 170 | 171 | 172 | 173 | condition = $"@({condition})"; 174 | var conditionelement = new XElement("choose", new XElement("when", "")); 175 | conditionelement.Element("when").SetAttributeValue("condition", HttpUtility.HtmlDecode(condition)); 176 | conditionelement.Element("when").Add(policy); 177 | return conditionelement; 178 | } 179 | else 180 | return policy; 181 | } 182 | 183 | private string GetDataTypeFromStringValue(string str) 184 | { 185 | bool boolValue; 186 | Int32 intValue; 187 | Int64 bigintValue; 188 | double doubleValue; 189 | DateTime dateValue; 190 | 191 | if (bool.TryParse(str, out boolValue)) 192 | return "bool"; 193 | else if (Int32.TryParse(str, out intValue)) 194 | return "int"; 195 | else if (Int64.TryParse(str, out bigintValue)) 196 | return "Int64"; 197 | else if (double.TryParse(str, out doubleValue)) 198 | return "double"; 199 | else if (DateTime.TryParse(str, out dateValue)) 200 | return "DateTime"; 201 | else return "string"; 202 | 203 | } 204 | 205 | 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/ApigeeToAzureApimMigrationTool.Service.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/ApigeeXmlFileLoader.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Interface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using System.Xml.Linq; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Service 10 | { 11 | public class ApigeeXmlFileLoader : IApigeeXmlLoader 12 | { 13 | private readonly IBundleProvider _bundleProvider; 14 | public ApigeeXmlFileLoader(IBundleProvider bundle) 15 | { 16 | _bundleProvider = bundle; 17 | } 18 | public XDocument LoadProxyXml(string proxyName) 19 | { 20 | var bundle = _bundleProvider.GetApiProxyBundle(proxyName); 21 | return XDocument.Load(Path.Combine(bundle.GetBundlePath(), $"{proxyName}.xml")); 22 | } 23 | 24 | public XDocument LoadTargetXml(string proxyName, string targetEndpointName) 25 | { 26 | var bundle = _bundleProvider.GetApiProxyBundle(proxyName); 27 | return XDocument.Load(Path.Combine(bundle.GetBundlePath(), "targets", $"{targetEndpointName}.xml")); 28 | } 29 | 30 | public XDocument LoadProxyEndpointXml(string proxyName, string proxyEndpointName) 31 | { 32 | var bundle = _bundleProvider.GetApiProxyBundle(proxyName); 33 | return XDocument.Load(Path.Combine(bundle.GetBundlePath(), "proxies", $"{proxyEndpointName}.xml")); 34 | } 35 | 36 | public XDocument LoadPolicyXml(string proxyName, string policyName) 37 | { 38 | var bundle = _bundleProvider.GetApiProxyBundle(proxyName); 39 | return XDocument.Load(Path.Combine(bundle.GetBundlePath(), "policies", $"{policyName}.xml")); 40 | } 41 | 42 | public XDocument LoadSharedFlowBundleXml(string sharedFlowName) 43 | { 44 | var bundle = _bundleProvider.GetSharedFlowBundle(sharedFlowName); 45 | return XDocument.Load(Path.Combine(bundle.GetBundlePath(), $"{sharedFlowName}.xml")); 46 | } 47 | 48 | public XDocument LoadSharedFlowXml(string sharedFlowName, string sharedFlowFileName) 49 | { 50 | var bundle = _bundleProvider.GetSharedFlowBundle(sharedFlowName); 51 | return XDocument.Load(Path.Combine(bundle.GetBundlePath(), "sharedflows", $"{sharedFlowFileName}.xml")); 52 | } 53 | 54 | public XDocument LoadSharedFlowPolicyXml(string sharedFlowName, string policyName) 55 | { 56 | var bundle = _bundleProvider.GetSharedFlowBundle(sharedFlowName); 57 | return XDocument.Load(Path.Combine(bundle.GetBundlePath(), "policies", $"{policyName}.xml")); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Bundles/ApigeeFileApiProxyBundle.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Service.Bundles 10 | { 11 | public class ApigeeFileApiProxyBundle : IBundle 12 | { 13 | private string _bundleBasePath; 14 | private string _proxyOrProductName; 15 | 16 | public ApigeeFileApiProxyBundle(string bundleBasePath, string proxyOrProductName) 17 | { 18 | _bundleBasePath = bundleBasePath; 19 | _proxyOrProductName = proxyOrProductName; 20 | } 21 | 22 | public string GetBundlePath() 23 | { 24 | if (string.IsNullOrEmpty(_proxyOrProductName)) 25 | { 26 | throw new Exception("API Proxy bundle not loaded. Please load the bundle first"); 27 | } 28 | 29 | return Path.Combine(_bundleBasePath, _proxyOrProductName, "apiproxy"); 30 | } 31 | 32 | public Task LoadBundle() 33 | { 34 | // Nothing to return, the bundle is already in the filesystem 35 | return Task.CompletedTask; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Bundles/ApigeeFileBundleProvider.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Interface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace ApigeeToAzureApimMigrationTool.Service.Bundles 9 | { 10 | public class ApigeeFileBundleProvider : IBundleProvider 11 | { 12 | private readonly IDictionary _apiProxyBundles; 13 | private readonly IDictionary _sharedFlowBundles; 14 | 15 | private readonly string _bundleBasePath; 16 | 17 | public ApigeeFileBundleProvider(string bundleBasePath) 18 | { 19 | _apiProxyBundles = new Dictionary(); 20 | _sharedFlowBundles = new Dictionary(); 21 | 22 | _bundleBasePath = bundleBasePath; 23 | } 24 | 25 | public IBundle GetApiProxyBundle(string proxyOrProductName) 26 | { 27 | if (!_apiProxyBundles.ContainsKey(proxyOrProductName)) 28 | { 29 | _apiProxyBundles.Add(proxyOrProductName, new ApigeeFileApiProxyBundle(_bundleBasePath, proxyOrProductName)); 30 | } 31 | return _apiProxyBundles[proxyOrProductName]; 32 | } 33 | 34 | public IBundle GetSharedFlowBundle(string sharedFlowName) 35 | { 36 | if (!_sharedFlowBundles.ContainsKey(sharedFlowName)) 37 | { 38 | _sharedFlowBundles.Add(sharedFlowName, new ApigeeFileSharedFlowBundle(_bundleBasePath, sharedFlowName)); 39 | } 40 | return _sharedFlowBundles[sharedFlowName]; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Bundles/ApigeeFileSharedFlowBundle.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Service 10 | { 11 | public class ApigeeFileSharedFlowBundle : IBundle 12 | { 13 | private string _bundleBasePath; 14 | private string _sharedFlowName; 15 | 16 | public ApigeeFileSharedFlowBundle(string bundleBasePath, string sharedFlowName) 17 | { 18 | _bundleBasePath = bundleBasePath; 19 | _sharedFlowName = sharedFlowName; 20 | } 21 | 22 | public string GetBundlePath() 23 | { 24 | if (string.IsNullOrEmpty(_sharedFlowName)) 25 | { 26 | throw new Exception("Shared flow bundle not loaded. Please load the bundle first"); 27 | } 28 | 29 | return Path.Combine(_bundleBasePath, _sharedFlowName, "sharedflowbundle"); 30 | } 31 | 32 | public Task LoadBundle() 33 | { 34 | // Nothing to return, the bundle is already in the filesystem 35 | return Task.CompletedTask; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Bundles/ApigeeOnlineApiProxyBundle.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Service.Bundles 10 | { 11 | public class ApigeeOnlineApiProxyBundle : IBundle 12 | { 13 | private readonly IApigeeManagementApiService _apigeeManagementApiService; 14 | private readonly string _basePath; 15 | private readonly string _proxyOrProductName; 16 | private string? _bundlePath; 17 | 18 | public ApigeeOnlineApiProxyBundle(string basePath, string proxyOrProductName, IApigeeManagementApiService apigeeManagementApiService) 19 | { 20 | _basePath = basePath; 21 | _proxyOrProductName = proxyOrProductName; 22 | _apigeeManagementApiService = apigeeManagementApiService; 23 | } 24 | 25 | public async Task LoadBundle() 26 | { 27 | //get api metadata 28 | var apiProxyMetadata = await _apigeeManagementApiService.GetApiProxyByName(_proxyOrProductName); 29 | //get the latest revision 30 | int maxRevision = apiProxyMetadata.revision.Select(x => int.Parse(x)).Max(); 31 | //download api proxy bundle 32 | _bundlePath = await _apigeeManagementApiService.DownloadApiProxyBundle(_basePath, _proxyOrProductName, maxRevision); 33 | } 34 | public string GetBundlePath() 35 | { 36 | if (string.IsNullOrEmpty(_bundlePath)) 37 | { 38 | throw new Exception("Bundle not loaded. Please load the bundle first"); 39 | } 40 | 41 | return Path.Combine(_basePath, _proxyOrProductName, "apiproxy"); 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Bundles/ApigeeOnlineBundleProvider.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Service.Bundles 10 | { 11 | public class ApigeeOnlineBundleProvider : IBundleProvider 12 | { 13 | private readonly IApigeeManagementApiService _apigeeManagementApiService; 14 | private readonly IDictionary _apiProxyBundles; 15 | private readonly IDictionary _sharedFlowBundles; 16 | 17 | private readonly string _bundleBasePath; 18 | 19 | public ApigeeOnlineBundleProvider(string bundleBasePath, IApigeeManagementApiService apigeeManagementApiService) 20 | { 21 | _apigeeManagementApiService = apigeeManagementApiService; 22 | _apiProxyBundles = new Dictionary(); 23 | _sharedFlowBundles = new Dictionary(); 24 | 25 | _bundleBasePath = bundleBasePath; 26 | } 27 | 28 | public IBundle GetApiProxyBundle(string proxyOrProductName) 29 | { 30 | if (!_apiProxyBundles.ContainsKey(proxyOrProductName)) 31 | { 32 | _apiProxyBundles.Add(proxyOrProductName, new ApigeeOnlineApiProxyBundle(_bundleBasePath, proxyOrProductName, _apigeeManagementApiService)); 33 | } 34 | return _apiProxyBundles[proxyOrProductName]; 35 | } 36 | 37 | public IBundle GetSharedFlowBundle(string sharedFlowName) 38 | { 39 | if (!_sharedFlowBundles.ContainsKey(sharedFlowName)) 40 | { 41 | _sharedFlowBundles.Add(sharedFlowName, new ApigeeOnlineSharedFlowBundle(_bundleBasePath, sharedFlowName, _apigeeManagementApiService)); 42 | } 43 | return _sharedFlowBundles[sharedFlowName]; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Bundles/ApigeeOnlineSharedFlowBundle.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace ApigeeToAzureApimMigrationTool.Service.Bundles 10 | { 11 | public class ApigeeOnlineSharedFlowBundle : IBundle 12 | { 13 | private readonly IApigeeManagementApiService _apigeeManagementApiService; 14 | private string _bundleBasePath; 15 | private string _sharedFlowName; 16 | private string? _bundlePath; 17 | 18 | public ApigeeOnlineSharedFlowBundle(string bundleBasePath, string sharedFlowName, IApigeeManagementApiService apigeeManagementApiService) 19 | { 20 | _bundleBasePath = bundleBasePath; 21 | _sharedFlowName = sharedFlowName; 22 | _apigeeManagementApiService = apigeeManagementApiService; 23 | } 24 | 25 | public async Task LoadBundle() 26 | { 27 | //get api metadata 28 | var sharedFlowMetadata = await _apigeeManagementApiService.GetSharedFlowByName(_sharedFlowName); 29 | //get the latest revision 30 | int maxRevision = sharedFlowMetadata.revision.Select(x => int.Parse(x)).Max(); 31 | //download api proxy bundle 32 | _bundlePath = await _apigeeManagementApiService.DownloadSharedFlowBundle(_bundleBasePath, _sharedFlowName, maxRevision); 33 | } 34 | public string GetBundlePath() 35 | { 36 | if (string.IsNullOrEmpty(_bundlePath)) 37 | { 38 | throw new Exception("Shared flow bundle not loaded. Please load the bundle first"); 39 | } 40 | 41 | return Path.Combine(_bundleBasePath, _sharedFlowName, "sharedflowbundle"); 42 | 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/ExpressionTranslator.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Interface; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Net; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | using System.Threading.Tasks; 10 | using System.Web; 11 | 12 | namespace ApigeeToAzureApimMigrationTool.Service 13 | { 14 | 15 | 16 | public class ExpressionTranslator : IExpressionTranslator 17 | { 18 | private readonly Dictionary _translationTable; 19 | private readonly Dictionary _translationTableForConditions; 20 | 21 | public ExpressionTranslator() 22 | { 23 | _translationTable = CreateTranslationTable(); 24 | _translationTableForConditions = CreateTranslationTableForConditions(); 25 | } 26 | 27 | /// 28 | /// Translates the whole string by replacing the keys in the translation table with their corresponding values. 29 | /// 30 | /// The input expression to be translated. 31 | /// The translated expression. 32 | public string TranslateWholeString(string expression) 33 | { 34 | foreach (var item in _translationTable) 35 | expression = expression.Replace(item.Key, item.Value); 36 | 37 | foreach (var item in _translationTableForConditions) 38 | expression = expression.Replace(item.Key, item.Value); 39 | 40 | return expression; 41 | } 42 | 43 | public string TranslateConditionOperator (string expression) 44 | { 45 | foreach (var item in _translationTableForConditions) 46 | expression = expression.Replace(item.Key, item.Value); 47 | 48 | return expression; 49 | } 50 | 51 | /// 52 | /// Checks if the content has variables in it by using a regular expression pattern. 53 | /// 54 | /// The input content to be checked. 55 | /// True if the content has variables, otherwise false. 56 | public bool ContentHasVariablesInIt(string content) 57 | { 58 | const string apigeeVariable = @"{(.*?)}"; 59 | return Regex.Matches(content, apigeeVariable).Any(); 60 | } 61 | 62 | /// 63 | /// Translates a single item by looking up its value in the translation table. 64 | /// 65 | /// The input expression to be translated. 66 | /// The default value to use in case variable wasn't found. 67 | /// The translated expression if found in the translation table, otherwise the original expression. 68 | public string TranslateSingleItem(string expression, string defaultValue = "") 69 | { 70 | string result = _translationTable.ContainsKey(expression) ? _translationTable[expression] : expression; 71 | 72 | if (result.Contains("request.header.")) 73 | { 74 | var headerName = result.Replace("request.header.", ""); 75 | result = $"context.Request.Headers.GetValueOrDefault(\"{headerName}\",\"\")"; 76 | } 77 | 78 | return result ; 79 | } 80 | 81 | /// 82 | /// Creates the translation table with the predefined key-value pairs. 83 | /// 84 | /// The translation table. 85 | private Dictionary CreateTranslationTable() 86 | { 87 | var expressionList = new Dictionary(); 88 | expressionList.Add("request.verb", "context.Operation.Method"); 89 | return expressionList; 90 | } 91 | 92 | /// 93 | /// Creates the translation table for conditions with the predefined key-value pairs. 94 | /// 95 | /// The translation table for conditions. 96 | private Dictionary CreateTranslationTableForConditions() 97 | { 98 | var expressionList = new Dictionary(); 99 | expressionList.Add(" AND ", " && "); 100 | expressionList.Add(" and ", " && "); 101 | expressionList.Add(" or ", " || "); 102 | expressionList.Add(" OR ", " || "); 103 | expressionList.Add(" = ", " == "); 104 | expressionList.Add(" =| ", " == "); 105 | 106 | return expressionList; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/AccessControlTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | 11 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 12 | { 13 | public class AccessControlTransformation : IPolicyTransformation 14 | { 15 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 16 | { 17 | var policyList = new List(); 18 | var ipRules = element.Element("IPRules"); 19 | var matchRules = ipRules.Elements("MatchRule"); 20 | foreach (var matchRule in matchRules) 21 | { 22 | var newPolicy = new XElement("ip-filter"); 23 | var action = matchRule.Attribute("action").Value.Equals("DENY", StringComparison.InvariantCultureIgnoreCase) ? "forbid" : "allow"; 24 | newPolicy.Add(new XAttribute("action", action)); 25 | foreach (var sourceAddress in matchRule.Elements("SourceAddress")) 26 | { 27 | var address = sourceAddress.Value; 28 | var mask = sourceAddress.Attribute("mask")?.Value; 29 | if (mask == null) 30 | { 31 | if (address.StartsWith("{")) 32 | { 33 | newPolicy.Add(new XElement("address", $"@(context.Variables.GetValueOrDefault(\"{address}\",\"\")")); 34 | } 35 | else 36 | newPolicy.Add(new XElement("address", address)); 37 | } 38 | else 39 | { 40 | //TODO: add support for variable used in mask 41 | var ipnetwork = System.Net.IPNetwork2.Parse($"{address}/{mask}"); 42 | var addressRangeElement = new XElement("address-range"); 43 | addressRangeElement.Add(new XAttribute("from", ipnetwork.FirstUsable), new XAttribute("to", ipnetwork.LastUsable)); 44 | newPolicy.Add(addressRangeElement); 45 | } 46 | } 47 | policyList.Add(newPolicy); 48 | } 49 | return Task.FromResult>(policyList); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/BasicAuthenticationTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 11 | { 12 | public class BasicAuthenticationTransformation : IPolicyTransformation 13 | { 14 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 15 | { 16 | var policyList = new List(); 17 | var basicAuthenticationPolicy = new XElement("authentication-basic"); 18 | 19 | var username = element.Element("User")?.Attribute("ref")?.Value; 20 | var password = element.Element("Password")?.Attribute("ref")?.Value; 21 | 22 | string usernameValue; 23 | string passwordValue; 24 | 25 | var operation = element.Element("Operation")?.Value; 26 | if (operation.Equals("Decode", StringComparison.InvariantCultureIgnoreCase)) 27 | { 28 | var source = element.Element("Source").Value; 29 | string sourceValue=null; 30 | if (source.StartsWith("request.header.")) 31 | { 32 | sourceValue = $"context.Request.Headers.GetValueOrDefault(\"{source.Replace("request.header.", "")}\")"; 33 | } 34 | 35 | var userNameVariablePolicy = new XElement("set-variable"); 36 | userNameVariablePolicy.Add(new XAttribute("name", username)); 37 | userNameVariablePolicy.Add(new XAttribute("value", $@"@{{ 38 | Encoding encoding = Encoding.GetEncoding(""iso-8859-1""); 39 | string usernamePassword = encoding.GetString(Convert.FromBase64String({sourceValue})); 40 | int seperatorIndex = usernamePassword.IndexOf(':'); 41 | return usernamePassword.Substring(0, seperatorIndex); 42 | }}")); 43 | 44 | policyList.Add(userNameVariablePolicy); 45 | 46 | var PasswordVariablePolicy = new XElement("set-variable"); 47 | PasswordVariablePolicy.Add(new XAttribute("name", password)); 48 | PasswordVariablePolicy.Add(new XAttribute("value", $@"@{{ 49 | Encoding encoding = Encoding.GetEncoding(""iso-8859-1""); 50 | string usernamePassword = encoding.GetString(Convert.FromBase64String({sourceValue})); 51 | int seperatorIndex = usernamePassword.IndexOf(':'); 52 | return usernamePassword.Substring(seperatorIndex + 1); 53 | }}")); 54 | 55 | policyList.Add(PasswordVariablePolicy); 56 | 57 | } 58 | else 59 | { 60 | if (username.StartsWith("request.header")) 61 | usernameValue = $"@(context.Request.Headers.GetValueOrDefault(\"{username.Replace("request.header.", "")}\"))"; 62 | else if (username.StartsWith("request.queryparam")) 63 | usernameValue = $"@(context.Url.Query.GetValueOrDefault(\"{username.Replace("request.queryparam.", "")}\"))"; 64 | else 65 | usernameValue = $"@(context.Variables.GetValueOrDefault(\"{username}\",\"\"))"; 66 | 67 | if (password.StartsWith("request.header")) 68 | passwordValue = $"@(context.Request.Headers.GetValueOrDefault(\"{password.Replace("request.header.", "")}\"))"; 69 | else if (password.StartsWith("request.queryparam")) 70 | passwordValue = $"@(context.Url.Query.GetValueOrDefault(\"{password.Replace("request.queryparam.", "")}\"))"; 71 | else 72 | passwordValue = $"@(context.Variables.GetValueOrDefault(\"{password}\",\"\"))"; 73 | 74 | basicAuthenticationPolicy.Add(new XAttribute("username", usernameValue), new XAttribute("password", passwordValue)); 75 | 76 | policyList.Add(basicAuthenticationPolicy); 77 | } 78 | 79 | return Task.FromResult>(policyList); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/ExtractVariablesTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.Design; 6 | using System.Linq; 7 | using System.Linq.Expressions; 8 | using System.Reflection.PortableExecutable; 9 | using System.Text; 10 | using System.Text.RegularExpressions; 11 | using System.Threading.Tasks; 12 | using System.Xml; 13 | using System.Xml.Linq; 14 | 15 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 16 | { 17 | public class ExtractVariablesTransformation : IPolicyTransformation 18 | { 19 | private readonly IList> _policyVariables; 20 | 21 | /// 22 | /// Initializes a new instance of the class. 23 | /// 24 | /// The list of policy variables. 25 | public ExtractVariablesTransformation(IList> policyVariables) 26 | { 27 | _policyVariables = policyVariables; 28 | } 29 | 30 | /// 31 | /// Transforms the given XML element into a collection of Azure API Management policy elements. 32 | /// 33 | /// The XML element representing the Apigee policy. 34 | /// The name of the Apigee policy. 35 | /// A task that represents the asynchronous transformation operation. 36 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 37 | { 38 | var apimPolicyElements = new List(); 39 | apimPolicyElements.AddRange(ExtractJsonValue(element, apigeePolicyName, policyDirection)); 40 | return Task.FromResult(apimPolicyElements.AsEnumerable()); 41 | } 42 | 43 | /// 44 | /// Extracts the JSON value from the XML element and creates a new Azure API Management policy element. 45 | /// 46 | /// The XML element representing the Apigee policy. 47 | /// The name of the Apigee policy. 48 | /// The new Azure API Management policy element. 49 | private List ExtractJsonValue(XElement element, string policyName, PolicyDirection policyDirection) 50 | { 51 | List policies = new List(); 52 | var variablePrefix = element.Element("VariablePrefix") == null ? string.Empty : element.Element("VariablePrefix").Value; 53 | string sourceName = element.Element("Source")?.Value; 54 | bool isSourceVariable = false; 55 | 56 | if (string.IsNullOrEmpty(sourceName)) 57 | { 58 | if (policyDirection == PolicyDirection.Inbound) 59 | sourceName = "Request"; 60 | else if (policyDirection == PolicyDirection.Outbound) 61 | sourceName = "Response"; 62 | } 63 | else 64 | { 65 | if (!sourceName.Equals("request") && !sourceName.Equals("response")) 66 | isSourceVariable = true; 67 | } 68 | 69 | if (sourceName.Equals("request")) 70 | sourceName = "Request"; 71 | if (sourceName.Equals("response")) 72 | sourceName = "Response"; 73 | 74 | if (element.Element("JSONPayload") != null) 75 | { 76 | var payloadElement = element.Element("JSONPayload"); 77 | foreach (var variableElement in payloadElement.Elements("Variable")) 78 | { 79 | string translatedExpression = string.Empty; 80 | if (isSourceVariable) 81 | { 82 | translatedExpression = "context.Variables.GetValueOrDefault(\"" + sourceName + "\")"; 83 | } 84 | else 85 | { 86 | translatedExpression = $"context.{sourceName}.Body.As(preserveContent: true)"; 87 | } 88 | 89 | var variableName = variableElement.Attribute("name").Value; 90 | var jsonPath = variableElement.Element("JSONPath").Value; 91 | 92 | string apimExpression = "@{" + 93 | $"JObject json = JObject.Parse({translatedExpression});" + 94 | "var extractedValue = json.SelectToken(\"" + jsonPath + "\");" + 95 | "return extractedValue;" + 96 | "}"; 97 | string apimVariableName = string.IsNullOrEmpty(variablePrefix) ? variableName : $"{variablePrefix}.{variableName}"; 98 | policies.Add(new XElement("set-variable", new XAttribute("name", apimVariableName), new XAttribute("value", apimExpression))); 99 | _policyVariables.Add(new KeyValuePair(policyName, variableName)); 100 | } 101 | } 102 | else if (element.Element("Header") != null) 103 | { 104 | string headerName = element.Element("Header").Attribute("name").Value; 105 | bool IgnoreCaseInPattern = Convert.ToBoolean(element.Element("Header").Element("Pattern").Attribute("ignoreCase").Value); 106 | string patternValue = element.Element("Header").Element("Pattern").Value; 107 | 108 | string patternRegex = @"{(.*?)}"; 109 | foreach (Match match in Regex.Matches(patternValue, patternRegex)) 110 | { 111 | if (match.Success && match.Groups.Count > 0) 112 | { 113 | string variableName = match.Groups[1].Value; 114 | string variablePattern = match.Groups[0].Value; 115 | string apimExpression = "@{" + 116 | $"string headerValue = \"\"; string pattern=\"{patternValue}\"" + 117 | $"headerValue = context.Request.Headers.GetValueOrDefault(\"{headerName}\");" + 118 | $"headerValue = headerValue.Replace(pattern.Replace(\"{variablePattern}\", \"\"),\"\");" + 119 | "return headerValue;" + 120 | "}"; 121 | string apimVariableName = string.IsNullOrEmpty(variablePrefix) ? variableName : $"{variablePrefix}.{variableName}"; 122 | policies.Add(new XElement("set-variable", new XAttribute("name", apimVariableName), new XAttribute("value", apimExpression))); 123 | } 124 | } 125 | } 126 | //TODO: support for multi params policies are not yet implemented 127 | else if (element.Elements("QueryParam") != null) 128 | { 129 | string queryParamName = element.Element("QueryParam").Attribute("name").Value; 130 | string patternValue = element.Element("QueryParam").Element("Pattern").Value; 131 | bool IgnoreCaseInPattern = Convert.ToBoolean(element.Element("QueryParam").Element("Pattern").Attribute("ignoreCase").Value); 132 | string patternRegex = @"{(.*?)}"; 133 | foreach (Match match in Regex.Matches(patternValue, patternRegex)) 134 | { 135 | if (match.Success && match.Groups.Count > 0) 136 | { 137 | string variableName = match.Groups[1].Value; 138 | string variablePattern = match.Groups[0].Value; 139 | string apimExpression = "@{" + 140 | $"string queryParamValue = \"\"; string pattern=\"{patternValue}\"" + 141 | $"queryParamValue = context.Request.Url.Query.GetValueOrDefault(\"{queryParamName}\");" + 142 | $"queryParamValue = queryParamValue.Replace(pattern.Replace(\"{variablePattern}\", \"\"),\"\");" + 143 | "return queryParamValue;" + 144 | "}"; 145 | string apimVariableName = string.IsNullOrEmpty(variablePrefix) ? variableName : $"{variablePrefix}.{variableName}"; 146 | policies.Add(new XElement("set-variable", new XAttribute("name", apimVariableName), new XAttribute("value", apimExpression))); 147 | } 148 | } 149 | } 150 | else if (element.Elements("XMLPayload") != null) 151 | { 152 | var payloadElement = element.Element("XMLPayload"); 153 | string namespaceName = payloadElement.Element("Namespaces").Element("Namespace").Value; 154 | string namespaceNamePrefix = payloadElement.Element("Namespaces").Element("Namespace").Attribute("prefix").Value; 155 | 156 | foreach (var variableElement in payloadElement.Elements("Variable")) 157 | { 158 | string translatedExpression = string.Empty; 159 | if (isSourceVariable) 160 | { 161 | translatedExpression = "context.Variables.GetValueOrDefault(\"" + sourceName + "\")"; 162 | } 163 | else 164 | { 165 | translatedExpression = $"context.{sourceName}.Body.As(preserveContent: true)"; 166 | } 167 | 168 | var variableName = variableElement.Attribute("name").Value; 169 | var xmlPath = variableElement.Element("XPath").Value; 170 | string apimExpression; 171 | if (!string.IsNullOrEmpty(namespaceName) && !string.IsNullOrEmpty(namespaceNamePrefix)) 172 | { 173 | apimExpression = "@{" + 174 | "XmlDocument doc = new XmlDocument();" + 175 | $"doc.LoadXml({translatedExpression});" + 176 | "XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);" + 177 | $"nsmgr.AddNamespace (\"{namespaceNamePrefix}\",\"{namespaceName}\");" + 178 | "var extractedValue = doc.SelectSingleNode(\"" + xmlPath + "\", nsmgr).Value;" + 179 | "return extractedValue;" + 180 | "}"; 181 | } 182 | else 183 | { 184 | apimExpression = "@{" + 185 | "XmlDocument doc = new XmlDocument();" + 186 | $"doc.LoadXml({translatedExpression});" + 187 | "var extractedValue = doc.SelectSingleNode(\"" + xmlPath + "\").Value;" + 188 | "return extractedValue;" + 189 | "}"; 190 | } 191 | string apimVariableName = string.IsNullOrEmpty(variablePrefix) ? variableName : $"{variablePrefix}.{variableName}"; 192 | policies.Add(new XElement("set-variable", new XAttribute("name", apimVariableName), new XAttribute("value", apimExpression))); 193 | _policyVariables.Add(new KeyValuePair(policyName, variableName)); 194 | } 195 | } 196 | 197 | return policies; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/FlowCalloutTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using ApigeeToAzureApimMigrationTool.Core; 3 | using ApigeeToAzureApimMigrationTool.Core.Enum; 4 | using ApigeeToAzureApimMigrationTool.Core.Interface; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Text; 10 | using System.Threading.Tasks; 11 | using System.Web; 12 | using System.Xml.Linq; 13 | 14 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 15 | { 16 | public class FlowCalloutTransformation : IPolicyTransformation 17 | { 18 | private readonly IApigeeXmlLoader _apigeeXmlLoader; 19 | private readonly IApimProvider _apimProvider; 20 | private readonly IApigeeManagementApiService _apiService; 21 | private readonly IBundleProvider _bundleProvider; 22 | private ApimConfiguration _apimConfiguration; 23 | private ApigeeConfiguration _apigeeConfiguration; 24 | 25 | private string _sharedFlowName; 26 | 27 | public FlowCalloutTransformation(IApigeeXmlLoader apigeeXmlLoader, IApimProvider apimProvider, IBundleProvider bundleProvider, IApigeeManagementApiService apiService, ApimConfiguration apimConfiguration, ApigeeConfiguration apigeeConfiguration) 28 | { 29 | _apigeeXmlLoader = apigeeXmlLoader; 30 | _apimProvider = apimProvider; 31 | _apiService = apiService; 32 | _bundleProvider = bundleProvider; 33 | _apimConfiguration = apimConfiguration; 34 | _apigeeConfiguration = apigeeConfiguration; 35 | } 36 | 37 | public async Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 38 | { 39 | var apimPolicies = new List(); 40 | 41 | _sharedFlowName = element.Element("SharedFlowBundle").Value; 42 | apimPolicies.Add(IncludeFragment(_sharedFlowName)); 43 | return apimPolicies.AsEnumerable(); 44 | } 45 | 46 | public async Task DonwloadAndTransformSharedFlow(IApimPolicyTransformer transformer) 47 | { 48 | string sharedFlowBundlePath = await DownloadSharedFlow(_sharedFlowName); 49 | await ImportSharedFlow(_sharedFlowName, _apimProvider.ApimName, transformer); 50 | } 51 | 52 | private async Task DownloadSharedFlow(string sharedFlowName) 53 | { 54 | var sharedFlowMetadata = await _apiService.GetSharedFlowByName(sharedFlowName); 55 | var bundle = _bundleProvider.GetSharedFlowBundle(sharedFlowName); 56 | await bundle.LoadBundle(); 57 | return await _apiService.DownloadSharedFlowBundle(bundle.GetBundlePath(), sharedFlowName, sharedFlowMetadata.revision.Select(x => int.Parse(x)).Max()); 58 | } 59 | 60 | private async Task ImportSharedFlow(string sharedflowName, string apimName, IApimPolicyTransformer apimPolicyTransformer) 61 | { 62 | var rawPolicyFragment = RawPolicyFragmentXml(); 63 | 64 | var sharedFlowBundleXml = _apigeeXmlLoader.LoadSharedFlowBundleXml(sharedflowName); 65 | var sharedFlowElement = sharedFlowBundleXml.Element("SharedFlowBundle"); 66 | string sharedFlowName = sharedFlowElement.Attribute("name").Value; 67 | string displayName = sharedFlowElement.Element("DisplayName").Value; 68 | string description = sharedFlowElement.Element("Description").Value; 69 | 70 | var sharedFlows = sharedFlowElement.Element("SharedFlows").Elements("SharedFlow"); 71 | 72 | foreach (var sharedFlow in sharedFlows) 73 | { 74 | var sharedFlowXml = _apigeeXmlLoader.LoadSharedFlowXml(sharedFlowName, sharedFlow.Value); 75 | var sharedFlowRootElement = sharedFlowXml.Element("SharedFlow"); 76 | var steps = sharedFlowRootElement.Elements("Step"); 77 | 78 | await apimPolicyTransformer.TransformPoliciesInCollection(steps, rawPolicyFragment.Root, _apigeeXmlLoader.LoadSharedFlowPolicyXml, apimName, sharedFlowName, _apigeeConfiguration, _apimConfiguration); 79 | await _apimProvider.CreatePolicyFragment(sharedFlowName, apimName, HttpUtility.HtmlDecode(rawPolicyFragment.ToString()), description); 80 | } 81 | } 82 | 83 | private XElement IncludeFragment(string fragmentName) 84 | { 85 | return new XElement("include-fragment", new XAttribute("fragment-id", fragmentName)); 86 | } 87 | 88 | private XDocument RawPolicyFragmentXml() 89 | { 90 | string rawFragment = @""; 91 | 92 | return XDocument.Parse(rawFragment); 93 | } 94 | 95 | 96 | 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/InvalidateCacheTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 11 | { 12 | 13 | public class InvalidateCacheTransformation : IPolicyTransformation 14 | { 15 | /// 16 | /// Transforms the Apigee cache policy to Azure APIM cache-remove-value policy. 17 | /// 18 | /// The XML element representing the Apigee cache policy. 19 | /// The name of the Apigee policy. 20 | /// The direction of the policy (inbound or outbound). 21 | /// A task that represents the asynchronous transformation operation. The task result contains the transformed Azure APIM policies as a collection of XML elements. 22 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 23 | { 24 | var apimPolicies = new List(); 25 | if (element.Element("CacheKey") != null) 26 | { 27 | apimPolicies.Add(CacheRemoveValue(element, apigeePolicyName)); 28 | } 29 | return Task.FromResult(apimPolicies.AsEnumerable()); 30 | } 31 | 32 | /// 33 | /// Transforms the Apigee cache key to Azure APIM cache-remove-value key. 34 | /// 35 | /// The XML element representing the Apigee cache policy. 36 | /// The name of the Apigee policy. 37 | /// The transformed Azure APIM cache-remove-value policy as an XML element. 38 | private XElement CacheRemoveValue(XElement element, string policyName) 39 | { 40 | var cacheKey = element.Element("CacheKey"); 41 | string prefix = cacheKey.Element("Prefix")?.Value; 42 | string keyFragment = cacheKey.Elements("KeyFragment").First(x => x.Attribute("ref") == null).Value; 43 | string keyFragmentRef = cacheKey.Elements("KeyFragment").First(x => x.Attribute("ref") != null).Attribute("ref").Value; 44 | 45 | string apimCacheKey; 46 | if (!string.IsNullOrEmpty(keyFragment)) 47 | apimCacheKey = keyFragment; 48 | else 49 | apimCacheKey = $"(string)context.Variables[\"{keyFragmentRef}\"]"; 50 | 51 | if (!string.IsNullOrEmpty(prefix)) 52 | apimCacheKey = $"{prefix}__{apimCacheKey}"; 53 | 54 | if (!string.IsNullOrEmpty(prefix) && !string.IsNullOrEmpty(keyFragmentRef)) 55 | apimCacheKey = $"@(\"{prefix}__\" + context.Variables.GetValueOrDefault(\"{keyFragmentRef}\"))"; 56 | 57 | var newPolicy = new XElement("cache-remove-value", new XAttribute("key", apimCacheKey)); 58 | return newPolicy; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/KeyValueMapTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Enum; 3 | using ApigeeToAzureApimMigrationTool.Core.Interface; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 12 | { 13 | /// 14 | /// Represents a transformation for converting Apigee key-value map policies to Azure API Management policies. 15 | /// 16 | public class KeyValueMapTransformation : IPolicyTransformation 17 | { 18 | private readonly IApimProvider _apimProvider; 19 | private readonly IApigeeManagementApiService _apigeeService; 20 | private readonly IList> _policyVariables; 21 | 22 | /// 23 | /// Initializes a new instance of the class. 24 | /// 25 | /// The Azure API Management provider. 26 | /// The Apigee management API service. 27 | /// The list of policy variables. 28 | public KeyValueMapTransformation(IApimProvider apimProvider, IApigeeManagementApiService apigeeService, IList> policyVariables) 29 | { 30 | _apimProvider = apimProvider; 31 | _apigeeService = apigeeService; 32 | _policyVariables = policyVariables; 33 | } 34 | 35 | /// 36 | /// Transforms the given Apigee element into a collection of Azure API Management policies. 37 | /// 38 | /// The Apigee element to transform. 39 | /// The name of the Apigee policy. 40 | /// A collection of Azure API Management policies as XElement objects. 41 | public async Task> Transform(XElement apigeeElement, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 42 | { 43 | var apimPolicies = new List(); 44 | 45 | foreach (var setVariableElement in await SetVariable(apigeeElement, _apigeeService.ProxyName, _apimProvider.ApimName, apigeePolicyName)) 46 | apimPolicies.Add(setVariableElement); 47 | 48 | return apimPolicies.AsEnumerable(); 49 | } 50 | 51 | /// 52 | /// Sets the variable values based on the Apigee element.Only Get operation is supported by APIM. Put and Delete are not supported. 53 | /// 54 | /// The Apigee element to process. 55 | /// The name of the proxy. 56 | /// The name of the Azure API Management instance. 57 | /// The name of the policy. 58 | /// A collection of Azure API Management policies as XElement objects. 59 | /// only Get operation is supported by APIM. Put and Delete are not supported. 60 | private async Task> SetVariable(XElement element, string proxyName, string apimName, string policyName) 61 | { 62 | XDocument setVariablePolicies = new XDocument(); 63 | setVariablePolicies.Add(new XElement("Root")); 64 | var mapIdentifier = element.Attribute("mapIdentifier").Value; 65 | 66 | var getElements = element.Elements("Get"); 67 | if (getElements != null) 68 | foreach (var getElement in getElements) 69 | { 70 | var index = getElement.Attribute("index").Value; 71 | string key = string.Empty; 72 | //ref attribute is not supported in APIM as it's not possible to construct the name of the named value in run time. 73 | //Alternative could be a logic apps or function app to read the named value 74 | if (getElement.Element("Key").Element("Parameter").Attribute("ref") != null) 75 | throw new Exception("using the ref attribute to retrieve the name of the parameter in run time is not supported"); 76 | else 77 | key = getElement.Element("Key").Element("Parameter").Value; 78 | 79 | 80 | var variableName = getElement.Attribute("assignTo").Value; 81 | 82 | var apigeeKeyValueMap = await _apigeeService.GetKeyValueMapByName(proxyName, _apigeeService.Environment, mapIdentifier); 83 | if (apigeeKeyValueMap != null) 84 | { 85 | var keyValueMapEntry = apigeeKeyValueMap.Entry.FirstOrDefault(x => x.Name.Equals(key)); 86 | if (keyValueMapEntry == null) 87 | throw new Exception($"Can't find entry {key} under mapIdentifier {mapIdentifier} in Apigee"); 88 | 89 | for (int i = 0; i < apigeeKeyValueMap.Entry.Count; i++) 90 | { 91 | var entry = apigeeKeyValueMap.Entry.ElementAt(i); 92 | await _apimProvider.AddNamedValue(apimName, proxyName, mapIdentifier, key, apigeeKeyValueMap.Encrypted, entry.Value, i + 1); 93 | } 94 | } 95 | 96 | string namedValueName = $"{mapIdentifier}-{key}-{index}"; 97 | namedValueName = namedValueName.Replace("_", "-"); 98 | 99 | if (namedValueName.Length > 80) 100 | { 101 | namedValueName = namedValueName.Substring(0, 80); 102 | namedValueName = _apimProvider.RemoveTrailingSpecialCharacters(namedValueName); 103 | } 104 | 105 | var policy = new XElement("set-variable", new XAttribute("name", variableName), new XAttribute("value", "{{" + namedValueName + "}}")); 106 | _policyVariables.Add(new KeyValuePair(policyName, variableName)); 107 | setVariablePolicies.Root.Add(policy); 108 | } 109 | return setVariablePolicies.Root.Elements(); 110 | 111 | } 112 | 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/LookupCacheTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 11 | { 12 | public class LookupCacheTransformation : IPolicyTransformation 13 | { 14 | private readonly IList> _policyVariables; 15 | 16 | public LookupCacheTransformation(IList> policyVariables) 17 | { 18 | _policyVariables = policyVariables; 19 | } 20 | 21 | /// 22 | /// Transforms the Apigee LookupCache policy to Azure APIM cache-lookup-value policy. 23 | /// 24 | /// The XML element representing the Apigee LookupCache policy. 25 | /// The name of the Apigee policy. 26 | /// A task that represents the asynchronous transformation operation. The task result contains the transformed Azure APIM policies. 27 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 28 | { 29 | var apimPolicies = new List(); 30 | if (element.Element("CacheKey") != null) 31 | { 32 | apimPolicies.Add(CacheLookupValue(element, apigeePolicyName)); 33 | } 34 | return Task.FromResult(apimPolicies.AsEnumerable()); 35 | } 36 | 37 | /// 38 | /// Transforms the CacheLookupValue element of the Apigee LookupCache policy to Azure APIM cache-lookup-value policy. 39 | /// 40 | /// The XML element representing the CacheLookupValue element of the Apigee LookupCache policy. 41 | /// The name of the Apigee policy. 42 | /// The transformed Azure APIM cache-lookup-value policy. 43 | private XElement CacheLookupValue(XElement element, string policyName) 44 | { 45 | var cacheKey = element.Element("CacheKey"); 46 | string prefix = cacheKey.Element("Prefix")?.Value; 47 | string keyFragment = cacheKey.Elements("KeyFragment").First(x => x.Attribute("ref") == null).Value; 48 | string keyFragmentRef = cacheKey.Elements("KeyFragment").First(x => x.Attribute("ref") != null).Attribute("ref").Value; 49 | string variableName = element.Element("AssignTo").Value; 50 | 51 | string apimCacheKey; 52 | if (!string.IsNullOrEmpty(keyFragment)) 53 | apimCacheKey = keyFragment; 54 | else 55 | apimCacheKey = $"(string)context.Variables[\"{keyFragmentRef}\"]"; 56 | 57 | if (!string.IsNullOrEmpty(prefix)) 58 | apimCacheKey = $"{prefix}__{apimCacheKey}"; 59 | 60 | if (!string.IsNullOrEmpty(prefix) && !string.IsNullOrEmpty(keyFragmentRef)) 61 | apimCacheKey = $"@(\"{prefix}__\" + context.Variables.GetValueOrDefault(\"{keyFragmentRef}\"))"; 62 | 63 | var newPolicy = new XElement("cache-lookup-value", new XAttribute("key", apimCacheKey), new XAttribute("variable-name", variableName)); 64 | _policyVariables.Add(new KeyValuePair(policyName, variableName)); 65 | return newPolicy; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/NullTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 11 | { 12 | public class NullTransformation : IPolicyTransformation 13 | { 14 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 15 | { 16 | // Return empty list 17 | return Task.FromResult(new List().AsEnumerable()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/OAuthV2Transformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using ApigeeToAzureApimMigrationTool.Core.Enum; 3 | using ApigeeToAzureApimMigrationTool.Core.Interface; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 12 | { 13 | 14 | 15 | public class OAuthV2Transformation : IPolicyTransformation 16 | { 17 | private ApimConfiguration _apimConfiguration; 18 | public OAuthV2Transformation(ApimConfiguration apimConfiguration) 19 | { 20 | _apimConfiguration = apimConfiguration; 21 | } 22 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 23 | { 24 | var apimPolicies = new List 25 | { 26 | OAuthV2(element, apigeePolicyName) 27 | }; 28 | 29 | return Task.FromResult(apimPolicies.AsEnumerable()); 30 | } 31 | private XElement OAuthV2(XElement element, string policyName) 32 | { 33 | //Only VerifyAccessToken operation is supported 34 | var operation = element.Element("Operation")?.Value; 35 | var name = element.Attribute("name")?.Value; 36 | if (!operation.Equals("VerifyAccessToken", StringComparison.InvariantCultureIgnoreCase)) 37 | { 38 | throw new NotSupportedException($"Only VerifyAccessToken operation is supported for OAuthV2 policy. Policy name: {name}"); 39 | } 40 | 41 | var displayName = element.Element("DisplayName")?.Value; 42 | string accessTokenPrefix = element.Element("AccessTokenPrefix")?.Value; 43 | if (string.IsNullOrEmpty(accessTokenPrefix)) 44 | accessTokenPrefix = "Bearer"; 45 | 46 | 47 | // query-parameter-name is not supported yet. JWT can only be included as a header 48 | string accessTokenHeaderName = element.Element("AccessToken")?.Value; 49 | if (string.IsNullOrEmpty(accessTokenHeaderName)) 50 | accessTokenHeaderName = "Authorization"; 51 | 52 | var newPolicy = new XElement("validate-jwt", 53 | new XAttribute("header-name", accessTokenHeaderName), 54 | new XAttribute("failed-validation-httpcode", "401"), 55 | new XAttribute("failed-validation-error-message", "Unauthorized. Access token is missing or invalid."), 56 | new XElement("openid-config", new XAttribute("url", $"https://login.microsoftonline.com/{_apimConfiguration.OAuthTenantId}/v2.0/.well-known/openid-configuration")) 57 | ); 58 | 59 | if (!string.IsNullOrEmpty(_apimConfiguration.OAuthAudiences)) 60 | { 61 | newPolicy.Element("validate-jwt").Add(new XElement("audiences")); 62 | 63 | var audienceArray = _apimConfiguration.OAuthAudiences.Split(','); 64 | foreach (string audience in audienceArray) 65 | { 66 | newPolicy.Element("validate-jwt").Element("audiences").Add(new XElement("audience", audience)); 67 | } 68 | } 69 | 70 | if (!string.IsNullOrEmpty(_apimConfiguration.OAuthIssuers)) 71 | { 72 | newPolicy.Element("validate-jwt").Add(new XElement("issuers")); 73 | 74 | var issuerArray = _apimConfiguration.OAuthIssuers.Split(','); 75 | foreach (string issuer in issuerArray) 76 | { 77 | newPolicy.Element("validate-jwt").Element("issuers").Add(new XElement("issuer", issuer)); 78 | } 79 | } 80 | 81 | string scopeClaimName = "scope"; 82 | if (!string.IsNullOrEmpty(_apimConfiguration.OAuthScopeClaimName)) 83 | scopeClaimName = _apimConfiguration.OAuthScopeClaimName; 84 | 85 | var scope = element.Element("Scope")?.Value; 86 | if (!string.IsNullOrEmpty(scope)) 87 | { 88 | newPolicy.Element("validate-jwt").Element("required-claims").Add( 89 | new XElement("claim", scope, 90 | new XAttribute("name", scopeClaimName), 91 | new XAttribute("match", "any"), 92 | new XAttribute("separator", " ") 93 | ) 94 | ); 95 | } 96 | 97 | return newPolicy; 98 | } 99 | 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/PolicyTransformationFactory.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToApimMigrationTool.Core.Config; 2 | using ApigeeToAzureApimMigrationTool.Core; 3 | using ApigeeToAzureApimMigrationTool.Core.Interface; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 12 | { 13 | public class PolicyTransformationFactory : IPolicyTransformationFactory 14 | { 15 | private readonly IApigeeXmlLoader _apigeeXmlLoader; 16 | private readonly IApigeeManagementApiService _apigeeService; 17 | private readonly IApimProvider _apimProvider; 18 | private readonly IBundleProvider _bundleProvider; 19 | private readonly IExpressionTranslator _expressionTranslator; 20 | public PolicyTransformationFactory(IApigeeManagementApiService apigeeService, IApimProvider apimProvider, IBundleProvider bundleProvider, IApigeeXmlLoader apigeeXmlLoader, IExpressionTranslator expressionTranslator) 21 | { 22 | _apigeeService = apigeeService; 23 | _apimProvider = apimProvider; 24 | _apigeeXmlLoader = apigeeXmlLoader; 25 | _bundleProvider = bundleProvider; 26 | _expressionTranslator = expressionTranslator; 27 | } 28 | public IPolicyTransformation GetTransformationForPolicy(string policyName, IList> policyVariables, ApigeeConfiguration apigeeConfiguration, ApimConfiguration apimConfig) 29 | { 30 | switch (policyName) 31 | { 32 | case "AssignMessage": 33 | return new AssignMessageTransformation(_expressionTranslator); 34 | case "LookupCache": 35 | return new LookupCacheTransformation(policyVariables); 36 | case "KeyValueMapOperations": 37 | return new KeyValueMapTransformation(_apimProvider, _apigeeService, policyVariables); 38 | case "VerifyJWT": 39 | return new VerifyJwtTransformation(policyVariables); 40 | case "ServiceCallout": 41 | return new ServiceCalloutTransformation(_apigeeService, _apimProvider, _expressionTranslator); 42 | case "ExtractVariables": 43 | return new ExtractVariablesTransformation(policyVariables); 44 | case "OAuthV2": 45 | return new OAuthV2Transformation(apimConfig); 46 | case "PopulateCache": 47 | return new PopulateCacheTransformation(policyVariables); 48 | case "InvalidateCache": 49 | return new InvalidateCacheTransformation(); 50 | case "FlowCallout": 51 | return new FlowCalloutTransformation(_apigeeXmlLoader, _apimProvider, _bundleProvider, _apigeeService, apimConfig, apigeeConfiguration); 52 | case "RaiseFault": 53 | return new RaiseFaultTransformation(_expressionTranslator); 54 | case "BasicAuthentication": 55 | return new BasicAuthenticationTransformation(); 56 | case "SpikeArrest": 57 | return new SpikeArrestTransformation(_expressionTranslator); 58 | case "AccessControl": 59 | return new AccessControlTransformation(); 60 | default: 61 | return new NullTransformation(); 62 | 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/PopulateCacheTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 12 | { 13 | 14 | public class PopulateCacheTransformation : IPolicyTransformation 15 | { 16 | private readonly IList> _policyVariables; 17 | 18 | public PopulateCacheTransformation(IList> policyVariables) 19 | { 20 | _policyVariables = policyVariables; 21 | } 22 | 23 | /// 24 | /// Transforms the Apigee policy to Azure API Management policies. 25 | /// 26 | /// The Apigee policy XML element. 27 | /// The name of the Apigee policy. 28 | /// The transformed Azure API Management policies as a collection of XML elements. 29 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 30 | { 31 | var apimPolicies = new List(); 32 | 33 | apimPolicies.Add(CacheStoreValue(element, apigeePolicyName)); 34 | 35 | return Task.FromResult(apimPolicies.AsEnumerable()); 36 | } 37 | 38 | /// 39 | /// Transforms the CacheStoreValue policy. 40 | /// 41 | /// The Apigee policy XML element. 42 | /// The name of the Apigee policy. 43 | /// The transformed Azure API Management policy as an XML element. 44 | private XElement CacheStoreValue(XElement element, string policyName) 45 | { 46 | var cacheKey = element.Element("CacheKey"); 47 | string prefix = cacheKey.Element("Prefix")?.Value; 48 | string keyFragment = cacheKey.Elements("KeyFragment").First(x => x.Attribute("ref") == null).Value; 49 | string keyFragmentRef = cacheKey.Elements("KeyFragment").First(x => x.Attribute("ref") != null).Attribute("ref").Value; 50 | 51 | string timeout = element.Element("ExpirySettings").Element("TimeoutInSec")?.Value; 52 | string timeoutRef = element.Element("ExpirySettings").Element("TimeoutInSec")?.Attribute("ref")?.Value; 53 | string expiryDate = element.Element("ExpirySettings").Element("ExpiryDate")?.Value; 54 | string expiryDateRef = element.Element("ExpirySettings").Element("ExpiryDate")?.Attribute("ref")?.Value; 55 | string timeOfDay = element.Element("ExpirySettings").Element("TimeOfDay")?.Value; 56 | string timeOfDayRef = element.Element("ExpirySettings").Element("TimeOfDay")?.Attribute("ref")?.Value; 57 | 58 | string timeOutSeconds = "30"; 59 | if (!string.IsNullOrEmpty(expiryDate)) 60 | { 61 | string[] formats = { "MM-dd-yyyy" }; 62 | DateTime cacheExpiryDate = DateTime.ParseExact(expiryDate, formats, new CultureInfo("en-US"), DateTimeStyles.None); 63 | timeOutSeconds = (cacheExpiryDate - DateTime.Now).Seconds.ToString(); 64 | } 65 | else if (!string.IsNullOrEmpty(expiryDateRef)) 66 | { 67 | timeOutSeconds = $"@((DateTime.Parse(context.Variables.GetValueOrDefault(\"{expiryDateRef}\")) - DateTime.Now).Seconds.ToString())"; 68 | } 69 | else if (!string.IsNullOrEmpty(timeOfDay)) 70 | { 71 | var time = TimeSpan.Parse(timeOfDay); 72 | var cacheExpiryDate = DateTime.Today.Add(time); 73 | timeOutSeconds = (cacheExpiryDate - DateTime.Now).Seconds.ToString(); 74 | } 75 | else if (!string.IsNullOrEmpty(timeOfDayRef)) 76 | { 77 | timeOutSeconds = $"@{{ var time = TimeSpan.Parse(context.Variables.GetValueOrDefault(\"{timeOfDayRef}\")); var cacheExpiryDate = DateTime.Today.Add(time); return (cacheExpiryDate - DateTime.Now).Seconds.ToString(); }}"; 78 | } 79 | else if (!string.IsNullOrEmpty(timeout)) 80 | { 81 | timeOutSeconds = timeout; 82 | } 83 | else if (!string.IsNullOrEmpty(timeoutRef)) 84 | { 85 | timeOutSeconds = $"@(context.Variables.GetValueOrDefault(\"{timeoutRef}\"))"; 86 | } 87 | else 88 | { 89 | throw new NotSupportedException("At least one of the following elements need to be defined within the ExpirySettings element of PopulateCache policy. TimeoutInSec, ExpiryDate or TimeOfDay"); 90 | } 91 | 92 | string variableName = element.Element("Source").Value; 93 | 94 | string apimCacheKey; 95 | if (!string.IsNullOrEmpty(keyFragment)) 96 | apimCacheKey = keyFragment; 97 | else 98 | apimCacheKey = $"(string)context.Variables[\"{keyFragmentRef}\"]"; 99 | 100 | if (!string.IsNullOrEmpty(prefix)) 101 | apimCacheKey = $"{prefix}__{apimCacheKey}"; 102 | 103 | if(!string.IsNullOrEmpty(prefix) && !string.IsNullOrEmpty(keyFragmentRef)) 104 | apimCacheKey = $"@(\"{prefix}__\" + context.Variables.GetValueOrDefault(\"{keyFragmentRef}\"))"; 105 | 106 | var newPolicy = new XElement("cache-store-value", new XAttribute("key", apimCacheKey), new XAttribute("value", $"@((string)context.Variables[\"{variableName}\"])"), new XAttribute("duration", timeOutSeconds)); 107 | _policyVariables.Add(new KeyValuePair(policyName, variableName)); 108 | return newPolicy; 109 | } 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/RaiseFaultTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 11 | { 12 | public class RaiseFaultTransformation : AssignMessageTransformation, IPolicyTransformation 13 | { 14 | public RaiseFaultTransformation(IExpressionTranslator expressionTranslator) : base(expressionTranslator) 15 | { 16 | } 17 | 18 | public Task> Transform(XElement apigeePolicyElement, string apigeePolicyName) 19 | { 20 | var policyList = new List(); 21 | var returnResponsePolicy = new XElement("return-response"); 22 | var apimPolicies = BuildApimPolicyCollection(apigeePolicyElement.Element("FaultResponse")).ToList(); 23 | string statusCode = "500"; 24 | string reasonPhrase = "Server Error"; 25 | 26 | string faultReason = string.Empty; 27 | if (apigeePolicyElement.Element("Set").Element("StatusCode") != null) 28 | statusCode = apigeePolicyElement.Element("Set").Element("StatusCode").Value; 29 | 30 | if (apigeePolicyElement.Element("Set").Element("ReasonPhrase") != null) 31 | reasonPhrase = apigeePolicyElement.Element("Set").Element("ReasonPhrase").Value; 32 | 33 | 34 | apimPolicies.Add(new XElement("set-status", new XAttribute("code", statusCode), new XAttribute("reason", reasonPhrase))); 35 | 36 | foreach(var policy in apimPolicies) 37 | { 38 | returnResponsePolicy.Add(policy); 39 | } 40 | 41 | policyList.Add(returnResponsePolicy); 42 | return Task.FromResult>(policyList); 43 | 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/ServiceCalloutTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core; 2 | using ApigeeToAzureApimMigrationTool.Core.Enum; 3 | using ApigeeToAzureApimMigrationTool.Core.Interface; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | using System.Threading.Tasks; 10 | using System.Xml.Linq; 11 | 12 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 13 | { 14 | public class ServiceCalloutTransformation : AssignMessageTransformation, IPolicyTransformation 15 | { 16 | private readonly IApigeeManagementApiService _apigeeService; 17 | private readonly IApimProvider _apimProvider; 18 | private readonly IExpressionTranslator _expressionTranslator; 19 | 20 | public ServiceCalloutTransformation(IApigeeManagementApiService apigeeService, IApimProvider apimProvider, IExpressionTranslator expressionTranslator) : base(expressionTranslator) 21 | { 22 | _apigeeService = apigeeService; 23 | _apimProvider = apimProvider; 24 | _expressionTranslator = expressionTranslator; 25 | } 26 | 27 | public async Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 28 | { 29 | var apimPolicies = new List(); 30 | 31 | var apimSendRequestPolicy = await SendRequest(element, _apimProvider.ApimUrl, _apigeeService.Environment); 32 | 33 | apimPolicies.Add(apimSendRequestPolicy); 34 | 35 | return apimPolicies.AsEnumerable(); 36 | } 37 | private async Task SendRequest(XElement element, string apimUrl, string? environment) 38 | { 39 | string requestVariable = element.Element("Request").Attribute("variable").Value; 40 | string responseVariable = element.Element("Response").Value; 41 | string continueOnError = element.Attribute("continueOnError").Value; 42 | string timeout = element.Element("Timeout")?.Value ?? "60"; 43 | 44 | var newPolicy = new XElement("send-request", new XAttribute("mode", $"new"), new XAttribute("response-variable-name", responseVariable), 45 | new XAttribute("timeout", timeout), new XAttribute("ignore-error", $"{continueOnError}")); 46 | 47 | string url = string.Empty; 48 | string targetServerName = string.Empty; 49 | 50 | if (element.Element("LocalTargetConnection") != null && element.Element("LocalTargetConnection").Element("Path") != null) 51 | url = element.Element("LocalTargetConnection").Element("Path").Value; 52 | else if (element.Element("HTTPTargetConnection") != null && element.Element("HTTPTargetConnection").Element("URL") != null) 53 | url = element.Element("HTTPTargetConnection").Element("URL").Value; 54 | else if (element.Element("HTTPTargetConnection") != null && element.Element("HTTPTargetConnection").Element("LoadBalancer") != null 55 | && element.Element("HTTPTargetConnection").Element("LoadBalancer").Elements("Server").Any()) 56 | { 57 | 58 | targetServerName = element.Element("HTTPTargetConnection").Element("LoadBalancer").Elements("Server").First().Attribute("name").Value; 59 | if (string.IsNullOrEmpty(environment)) 60 | throw new Exception($"service callout policy is using a load balancer as target connection. Environment input parameter must be provided in order to migrate target server {targetServerName}"); 61 | var targetServerResponse = await _apigeeService.GetTargetServerByName(targetServerName, environment); 62 | if (targetServerResponse == null) 63 | throw new Exception($"Can't read Target Server information for {targetServerName} in the {environment} env"); 64 | string protocol = "http://"; 65 | string port = string.Empty; 66 | if (targetServerResponse.SSLInfo.Enabled) 67 | protocol = "https://"; 68 | if (!(new int[] { 80, 443 }).Contains(targetServerResponse.Port)) 69 | port = $":{targetServerResponse.Port}"; 70 | 71 | url = $"{protocol}{targetServerResponse.Host}{port}"; 72 | } 73 | 74 | if (url.StartsWith('/')) 75 | url = apimUrl + url; 76 | 77 | if (element.Element("Request")?.Element("Set")?.Element("Path") != null) 78 | { 79 | string path = element.Element("Request").Element("Set").Element("Path").Value; 80 | if (path.StartsWith("{")) 81 | { 82 | url = url + "/" + $"@((string)context.Variables[\"{path.Replace("{", "").Replace("}", "")}\"])"; 83 | } 84 | } 85 | 86 | newPolicy.Add(new XElement("set-url", url)); 87 | 88 | string verb = element.Element("Request")?.Element("Set")?.Element("Verb") != null ? element.Element("Request").Element("Set").Element("Verb").Value : "GET"; 89 | newPolicy.Add(new XElement("set-method", verb)); 90 | 91 | var apimPolicies = BuildApimPolicyCollection(element.Element("Request")).ToList(); 92 | 93 | foreach (var policy in apimPolicies) 94 | { 95 | newPolicy.Add(policy); 96 | } 97 | 98 | return newPolicy; 99 | } 100 | 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/SpikeArrestTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Xml.Linq; 10 | 11 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 12 | { 13 | public class SpikeArrestTransformation : IPolicyTransformation 14 | { 15 | private readonly IExpressionTranslator _expressionTranslator; 16 | 17 | public SpikeArrestTransformation(IExpressionTranslator expressionTranslator) 18 | { 19 | _expressionTranslator = expressionTranslator; 20 | } 21 | 22 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 23 | { 24 | var policyList = new List(); 25 | 26 | string rateExpression; 27 | string renewalPeriodExpression = "1"; 28 | 29 | string rate = element.Element("Rate")?.Value; 30 | if (string.IsNullOrEmpty(rate)) 31 | { 32 | rate = element.Element("Rate").Attribute("ref").Value; 33 | rateExpression = $"@{{ var value = {_expressionTranslator.TranslateSingleItem(rate)}; return value.Contains(\"pm\") ? int.Parse(value.Replace(\"pm\", \"\")) / 60 : value.Replace(\"ps\", \"\"); }}"; 34 | renewalPeriodExpression = $"@{{ var value = {_expressionTranslator.TranslateSingleItem(rate)}; return value.Contains(\"pm\") ? 60 : 1 }}"; 35 | } 36 | else 37 | { 38 | if (rate.Contains("pm")) 39 | { 40 | renewalPeriodExpression = "60"; 41 | rateExpression = (int.Parse(rate.Replace("pm", "")) / 60).ToString(); 42 | } 43 | else 44 | rateExpression = (int.Parse(rate.Replace("ps", ""))).ToString(); 45 | } 46 | 47 | string counterKey = element.Element("Identifier")?.Attribute("ref")?.Value; 48 | 49 | XElement rateLimitPolicy; 50 | if (!string.IsNullOrEmpty(counterKey)) 51 | rateLimitPolicy = new XElement("rate-limit-by-key", new XAttribute("calls", rateExpression), new XAttribute("renewal-period", renewalPeriodExpression), 52 | new XAttribute("counter-key", $"@({_expressionTranslator.TranslateSingleItem(counterKey)})")); 53 | else 54 | rateLimitPolicy = new XElement("rate-limit", new XAttribute("calls", rateExpression), new XAttribute("renewal-period", renewalPeriodExpression)); 55 | 56 | policyList.Add(rateLimitPolicy); 57 | return Task.FromResult>(policyList); 58 | 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /ApigeeToAzureApimMigrationTool.Logic/Transformations/VerifyJwtTransformation.cs: -------------------------------------------------------------------------------- 1 | using ApigeeToAzureApimMigrationTool.Core.Enum; 2 | using ApigeeToAzureApimMigrationTool.Core.Interface; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | 10 | namespace ApigeeToAzureApimMigrationTool.Service.Transformations 11 | { 12 | public class VerifyJwtTransformation : IPolicyTransformation 13 | { 14 | private readonly IList> _policyVariables; 15 | 16 | public VerifyJwtTransformation(IList> policyVariables) 17 | { 18 | _policyVariables = policyVariables; 19 | } 20 | public Task> Transform(XElement element, string apigeePolicyName, PolicyDirection policyDirection = PolicyDirection.Inbound) 21 | { 22 | var apimPolicies = new List 23 | { 24 | ValidateJwt(element) 25 | }; 26 | 27 | return Task.FromResult(apimPolicies.AsEnumerable()); 28 | } 29 | private XElement ValidateJwt(XElement element) 30 | { 31 | var token = element.Element("Source").Value; 32 | var key = element.Element("PublicKey")?.Element("Value")?.Attribute("ref")?.Value; 33 | if (key == null) 34 | throw new NotSupportedException("VerifyJWT policy without defined public key is not supported"); 35 | 36 | var decryptionKeyelement = new XElement("issuer-signing-keys"); 37 | decryptionKeyelement.Add(new XElement("key", $"@(context.Variables.GetValueOrDefault(\"{key}\",\"\"))")); 38 | string outputVariableName = $"{token}_validated"; 39 | var newPolicy = new XElement("validate-jwt", new XAttribute("token-value", $"@(context.Variables.GetValueOrDefault(\"{token}\",\"\"))"), decryptionKeyelement, 40 | new XAttribute("output-token-variable-name", outputVariableName)); 41 | _policyVariables.Add(new KeyValuePair(element.Attribute("name").Value, outputVariableName)); 42 | 43 | if(element.Element("IgnoreInvalidToken") != null) 44 | { 45 | newPolicy.Add(new XAttribute("ignore-error", "true")); 46 | } 47 | 48 | 49 | 50 | return newPolicy; 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @pfekrati @jaspecla 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /Roadmap.md: -------------------------------------------------------------------------------- 1 | # Elevating the Apigee to APIM Roadmap Experience 2 | 3 | Welcome to [the comprehensive roadmap](https://github.com/orgs/Azure/projects/622/views/2) that grants you a panoramic insight into the ongoing developments within this repository. This structured guide is designed to streamline and enhance your understanding of the intricate journey from Apigee to APIM. 4 | 5 | ## Navigating the Roadmap 6 | 7 | Every element embedded in this roadmap is meticulously documented as an issue, with discernable labels providing crucial insights. These labels serve as a beacon, guiding you through the dynamic phases of our release cycle and shedding light on the anticipated milestones. 8 | 9 | ### Guide to Release Phases 10 | 11 | - **exploring**: Delving into the depths, assessing potential value and feasibility. 12 | - **in design**: Crafting the intricate specifications and design nuances. 13 | - **preview**: Unveiling the feature to a select audience for an exclusive preview. 14 | - **GA**: Grand unveiling, launching the feature to all users for widespread adoption. 15 | 16 | ### Product SKUs Insight 17 | 18 | Our roadmap is intricately interwoven with various product SKUs, each catering to specific facets of our overarching objectives. 19 | 20 | - **Product** 21 | - **Subscription** 22 | - **Policies** 23 | - **APIs** 24 | 25 | ## The Roadmap Unveiled 26 | 27 | As a feature evolves through its lifecycle, it is adorned with labels signifying the product SKUs it aligns with. Following delivery, the distinguished **shipped** label is affixed to the corresponding issue, officially marking its closure. A comment accompanies this closure, providing a seamless link to the pertinent release details. 28 | 29 | ## Orchestrating the Process 30 | 31 | This roadmap doesn't merely exist; it thrives within the framework of a meticulously reviewed process, with labels orchestrating its governance. These labels serve as guardians, tracking the crucial steps within the governance process: 32 | 33 | - **reviewed**: Scrutinized, ensuring a comprehensive understanding of the feature scope. 34 | - **endorsed**: Resources allocated, paving the way for progression. 35 | 36 | This roadmap guides you with confidence, as it not only outlines a path but also considers every detail, ensuring a smooth transition from discovery to widespread use. 37 | -------------------------------------------------------------------------------- /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) and [Xamarin](https://github.com/xamarin). 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/security.md/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/security.md/msrc/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/security.md/msrc/pgp). 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://www.microsoft.com/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/security.md/msrc/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/security.md/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /SUPPORT.md: -------------------------------------------------------------------------------- 1 | # TODO: The maintainer of this repo has not yet edited this file 2 | 3 | **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? 4 | 5 | - **No CSS support:** Fill out this template with information about how to file issues and get help. 6 | - **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. 7 | - **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. 8 | 9 | *Then remove this first heading from this SUPPORT.MD file before publishing your repo.* 10 | 11 | # Support 12 | 13 | ## How to file issues and get help 14 | 15 | This project uses GitHub Issues to track bugs and feature requests. Please search the existing 16 | issues before filing new issues to avoid duplicates. For new issues, file your bug or 17 | feature request as a new Issue. 18 | 19 | For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE 20 | FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER 21 | CHANNEL. WHERE WILL YOU HELP PEOPLE?**. 22 | 23 | ## Microsoft Support Policy 24 | 25 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above. 26 | --------------------------------------------------------------------------------