Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit 6092a93

Browse files
bremnesRupengLiu
authored andcommitted
[Extractor] Re-added command line arguments (#325)
* #319 Re-added support for command line arguments * Extractor, small refactoring and tests for ExtractorConfiguration
1 parent a0c2f81 commit 6092a93

File tree

8 files changed

+216
-127
lines changed

8 files changed

+216
-127
lines changed

src/APIM_ARMTemplate/README.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,11 @@ az account set --subscription <subscription_id>
294294
```
295295

296296
#### Extractor Arguments
297-
**Please go to "extractorparameters.json" to specify the parameters to pass in or pass an argument --extractorConfig which points to a file as defined below.**
297+
298+
You have two choices when specifying your settings:
299+
1. By using a json file with key-values where the keys matches the table below. Use the `extractorConfig` argument:
300+
`extract --extractorConfig c:/temp/extractSettings.json`. [See more examples.](#extractorParameterFileExamples)
301+
2. Pass the arguments on the command line. For instance `extract --sourceApimName my-feature-apim --destinationApimName company-stable-apim --resourceGroup my-feature-rg --fileFolder c:\temp\apim-extract --apiName MyFeatureV1Api`.
298302

299303
| Property | Required | Value |
300304
|-----------------------|-----------------------|---------------------------------------------------|
@@ -304,7 +308,8 @@ az account set --subscription <subscription_id>
304308
| fileFolder | Yes | Path to output folder |
305309
| apiName | No | Name of API. If provided, Extractor executes single API extraction. Otherwise, Extractor executes full extraction. Note: This is the "Name" value as seen in the API settings, not "Display Name" and is case sensitive. |
306310
| linkedTemplatesBaseUrl| No | Linked templates remote location. If provided, Extractor generates master template and requires linked templates pushed to remote location. |
307-
| linkedTemplatesUrlQueryString | No | String | Query string appended to linked templates uris that enables retrieval from private storage. |
311+
| linkedTemplatesUrlQueryString | No | Query string appended to linked templates uris that enables retrieval from private storage. |
312+
| linkedTemplatesSasToken | No | String appended to end of the linked templates uris that enables adding a SAS token or other query parameters. |
308313
| policyXMLBaseUrl | No | Policy XML files remote location. If provided, Extractor generates policies folder with xml files, and requires they be pushed to remote location. |
309314
| splitAPIs | No | If set to "true", then generate multiple api folders, each api will have a seperate folder, with a separate master template to deploy this api. If this single api has a version set, then a version set folder will generate instead, then all apis that belongs to this version set will be included in the version set folder, apis in this version set can be deployed separately using every api's master template, or they can be deployed together using the master template in "VersionSetMasterFolder" folder |
310315
| apiVersionSetName | No | Name of the APIVersionSet. If provided, extract all apis within this apiversionset. It will generate seperate folder for each api and also a master folder to link all apis in this apiversionset |
@@ -316,8 +321,10 @@ az account set --subscription <subscription_id>
316321
* Can not use "splitAPIs" and "apiName" at the same time, since using "apiName" only extract one API
317322
* Can not use "apiName" and "mutipleAPIs" at the same time
318323
* Can only "includeAllRevisions" with "apiName"
319-
### Extractor Parameter Example
320-
In **extractorparams.json** file (path: src/APIM_ARMTemplate/apimtemplate/extractorparams.json) <br />
324+
325+
<a name="extractorParameterFileExamples"></a>
326+
### Extractor Parameter File Example
327+
321328
Executing **a single API extraction with linked templates and policy file** generation, use the following parameters:
322329
```
323330
{
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Extract;
2+
using System;
3+
using Xunit;
4+
5+
namespace apimtemplate.test.Extractor
6+
{
7+
[Trait("Category", "Unit")]
8+
public class ExtractorConfigTests
9+
{
10+
[Fact]
11+
public void ExtractorConfigValidate_NoPropertiesSet_MissingParameterException()
12+
{
13+
var extractorConfig = new ExtractorConfig();
14+
15+
var expectedException = Assert.Throws<ArgumentException>(() => extractorConfig.Validate());
16+
17+
Assert.Contains("Missing parameter", expectedException.Message);
18+
}
19+
20+
[Fact]
21+
public void ExtractorConfigValidate_MinimumPropertiesSet_IsValid()
22+
{
23+
var extractorConfig = new ExtractorConfig
24+
{
25+
sourceApimName = "source-apim",
26+
destinationApimName = "destination-apim",
27+
resourceGroup = "resource-group",
28+
fileFolder = "c:/my/folder"
29+
};
30+
31+
extractorConfig.Validate();
32+
}
33+
34+
[Theory]
35+
[InlineData("true", null, "my-api", null, null)]
36+
[InlineData("true", "api-version-set", null, null, null)]
37+
[InlineData(null, "api-version-set", null, null, "true")]
38+
[InlineData(null, null, "my-api", null, "true")]
39+
[InlineData(null, "api-version-set", "my-api", null, null)]
40+
[InlineData(null, null, null, "true", null)]
41+
public void ExtractorConfigValidate_VerifyNotSupportedCases_ThrowsException(string splitApis, string apiVersionSetName, string apiName, string includeAllRevisions, string multipleApis)
42+
{
43+
var extractorConfig = new ExtractorConfig
44+
{
45+
sourceApimName = "source-apim",
46+
destinationApimName = "destination-apim",
47+
resourceGroup = "resource-group",
48+
fileFolder = "c:/my/folder",
49+
splitAPIs = splitApis,
50+
apiName = apiName,
51+
apiVersionSetName = apiVersionSetName,
52+
includeAllRevisions = includeAllRevisions,
53+
mutipleAPIs = multipleApis
54+
};
55+
56+
Assert.Throws<NotSupportedException>(() => extractorConfig.Validate());
57+
}
58+
}
59+
}

src/APIM_ARMTemplate/apimtemplate/Commands/Extract.cs

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.ComponentModel;
3+
using System.Linq;
24
using McMaster.Extensions.CommandLineUtils;
35
using Microsoft.Azure.Management.ApiManagement.ArmTemplates.Common;
46

@@ -10,22 +12,26 @@ public ExtractCommand()
1012
{
1113
this.Name = GlobalConstants.ExtractName;
1214
this.Description = GlobalConstants.ExtractDescription;
13-
var filePath = this.Option("--extractorConfig <extractorConfig>", "Config file of the extractor", CommandOptionType.SingleValue);
14-
15+
var extractorConfigFilePathOption = this.Option("--extractorConfig <extractorConfig>", "Config file of the extractor", CommandOptionType.SingleValue);
16+
AddExtractorConfigPropertiesToCommandLineOptions();
17+
1518
this.HelpOption();
1619

1720
this.OnExecute(async () =>
1821
{
19-
// convert config file to extractorConfig class
20-
FileReader fileReader = new FileReader();
21-
string extractorConfigPath = filePath.HasValue() ? filePath.Value().ToString() : null;
22+
ExtractorConfig extractorConfig = new ExtractorConfig();
23+
24+
if (extractorConfigFilePathOption.HasValue())
25+
{
26+
var fileReader = new FileReader();
27+
extractorConfig = fileReader.ConvertConfigJsonToExtractorConfig(extractorConfigFilePathOption.Value());
28+
}
2229

23-
ExtractorConfig extractorConfig = fileReader.ConvertConfigJsonToExtractorConfig(extractorConfigPath);
30+
UpdateExtractorConfigFromAdditionalArguments(extractorConfig);
2431

2532
try
2633
{
27-
//validation check
28-
ExtractorUtils.validationCheck(extractorConfig);
34+
extractorConfig.Validate();
2935

3036
string singleApiName = extractorConfig.apiName;
3137
bool splitAPIs = extractorConfig.splitAPIs != null && extractorConfig.splitAPIs.Equals("true");
@@ -89,5 +95,24 @@ public ExtractCommand()
8995
}
9096
});
9197
}
98+
99+
private void AddExtractorConfigPropertiesToCommandLineOptions()
100+
{
101+
foreach (var propertyInfo in typeof(ExtractorConfig).GetProperties())
102+
{
103+
var description = Attribute.IsDefined(propertyInfo, typeof(DescriptionAttribute)) ? (Attribute.GetCustomAttribute(propertyInfo, typeof(DescriptionAttribute)) as DescriptionAttribute).Description : string.Empty;
104+
105+
this.Option($"--{propertyInfo.Name} <{propertyInfo.Name}>", description, CommandOptionType.SingleValue);
106+
}
107+
}
108+
109+
private void UpdateExtractorConfigFromAdditionalArguments(ExtractorConfig extractorConfig)
110+
{
111+
var extractorConfigType = typeof(ExtractorConfig);
112+
foreach (var option in this.Options.Where(o => o.HasValue()))
113+
{
114+
extractorConfigType.GetProperty(option.LongName)?.SetValue(extractorConfig, option.Value());
115+
}
116+
}
92117
}
93-
}
118+
}

src/APIM_ARMTemplate/apimtemplate/Common/FileHandlers/FileReader.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,11 @@ public async Task<CreatorConfig> ConvertConfigYAMLToCreatorConfigAsync(string co
6262

6363
public ExtractorConfig ConvertConfigJsonToExtractorConfig(string extractorJsonPath)
6464
{
65-
if (extractorJsonPath == null)
65+
if (string.IsNullOrWhiteSpace(extractorJsonPath) || !File.Exists(extractorJsonPath))
6666
{
67-
extractorJsonPath = "extractorparams.json";
67+
throw new FileNotFoundException($"You have to specify an existing file, you specified: '{extractorJsonPath}'");
6868
}
69+
6970
using (StreamReader r = new StreamReader(extractorJsonPath))
7071
{
7172
string extractorJson = r.ReadToEnd();

src/APIM_ARMTemplate/apimtemplate/Creator/Models/ExtractorConfiguration.cs

Lines changed: 0 additions & 69 deletions
This file was deleted.
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System;
2+
using System.ComponentModel;
3+
4+
namespace Microsoft.Azure.Management.ApiManagement.ArmTemplates.Extract
5+
{
6+
public class ExtractorConfig
7+
{
8+
[Description("Source API Management name")]
9+
public string sourceApimName { get; set; }
10+
[Description("Destination API Management name")]
11+
public string destinationApimName { get; set; }
12+
[Description("Resource Group name")]
13+
public string resourceGroup { get; set; }
14+
[Description("ARM Template files folder")]
15+
public string fileFolder { get; set; }
16+
[Description("API name")]
17+
public string apiName { get; set; }
18+
[Description("Comma-separated list of API names")]
19+
public string mutipleAPIs { get; set; }
20+
[Description("Creates a master template with links")]
21+
public string linkedTemplatesBaseUrl { get; set; }
22+
public string linkedTemplatesSasToken { get; set; }
23+
[Description("Query string appended to linked templates uris that enables retrieval from private storage")]
24+
public string linkedTemplatesUrlQueryString { get; set; }
25+
[Description("Writes policies to local XML files that require deployment to remote folder")]
26+
public string policyXMLBaseUrl { get; set; }
27+
[Description("String appended to end of the linked templates uris that enables adding a SAS token or other query parameters")]
28+
public string policyXMLSasToken { get; set; }
29+
[Description("Split APIs into multiple templates")]
30+
public string splitAPIs { get; set; }
31+
[Description("Name of the apiVersionSet you want to extract")]
32+
public string apiVersionSetName { get; set; }
33+
[Description("Includes all revisions for a single api - use with caution")]
34+
public string includeAllRevisions { get; set; }
35+
36+
public void Validate()
37+
{
38+
if (string.IsNullOrEmpty(sourceApimName)) throw new ArgumentException("Missing parameter <sourceApimName>.");
39+
if (string.IsNullOrEmpty(destinationApimName)) throw new ArgumentException("Missing parameter <destinationApimName>.");
40+
if (string.IsNullOrEmpty(resourceGroup)) throw new ArgumentException("Missing parameter <resourceGroup>.");
41+
if (string.IsNullOrEmpty(fileFolder)) throw new ArgumentException("Missing parameter <filefolder>.");
42+
43+
bool shouldSplitAPIs = splitAPIs != null && splitAPIs.Equals("true");
44+
bool hasVersionSetName = apiVersionSetName != null;
45+
bool hasSingleApi = apiName != null;
46+
bool includeRevisions = includeAllRevisions != null && includeAllRevisions.Equals("true");
47+
bool hasMultipleAPIs = mutipleAPIs != null;
48+
49+
if (shouldSplitAPIs && hasSingleApi)
50+
{
51+
throw new NotSupportedException("Can't use splitAPIs and apiName at same time");
52+
}
53+
54+
if (shouldSplitAPIs && hasVersionSetName)
55+
{
56+
throw new NotSupportedException("Can't use splitAPIs and apiVersionSetName at same time");
57+
}
58+
59+
if ((hasVersionSetName || hasSingleApi) && hasMultipleAPIs)
60+
{
61+
throw new NotSupportedException("Can't use mutipleAPIs with apiName or apiVersionSetName at the same time");
62+
}
63+
64+
if (hasSingleApi && hasVersionSetName)
65+
{
66+
throw new NotSupportedException("Can't use apiName and apiVersionSetName at same time");
67+
}
68+
69+
if (!hasSingleApi && includeRevisions)
70+
{
71+
throw new NotSupportedException("\"includeAllRevisions\" can be used when you specify the API you want to extract with \"apiName\"");
72+
}
73+
}
74+
}
75+
76+
public class Extractor
77+
{
78+
public string sourceApimName { get; private set; }
79+
public string destinationApimName { get; private set; }
80+
public string resourceGroup { get; private set; }
81+
public string fileFolder { get; private set; }
82+
public string linkedTemplatesBaseUrl { get; private set; }
83+
public string linkedTemplatesSasToken { get; private set; }
84+
public string linkedTemplatesUrlQueryString { get; private set; }
85+
public string policyXMLBaseUrl { get; private set; }
86+
public string policyXMLSasToken { get; private set; }
87+
public string apiVersionSetName { get; private set; }
88+
public bool includeAllRevisions { get; private set; }
89+
90+
public Extractor(ExtractorConfig exc, string dirName)
91+
{
92+
this.sourceApimName = exc.sourceApimName;
93+
this.destinationApimName = exc.destinationApimName;
94+
this.resourceGroup = exc.resourceGroup;
95+
this.fileFolder = dirName;
96+
this.linkedTemplatesBaseUrl = exc.linkedTemplatesBaseUrl;
97+
this.linkedTemplatesSasToken = exc.linkedTemplatesSasToken;
98+
this.linkedTemplatesUrlQueryString = exc.linkedTemplatesUrlQueryString;
99+
this.policyXMLBaseUrl = exc.policyXMLBaseUrl;
100+
this.policyXMLSasToken = exc.policyXMLSasToken;
101+
this.apiVersionSetName = exc.apiVersionSetName;
102+
this.includeAllRevisions = exc.includeAllRevisions != null && exc.includeAllRevisions.Equals("true");
103+
}
104+
105+
public Extractor(ExtractorConfig exc): this(exc, exc.fileFolder)
106+
{
107+
}
108+
}
109+
}

0 commit comments

Comments
 (0)