Limitations of code generation & how to overcome them
Generator can not generate code for processing complex properties such as an array of objects. In that case developer has to extend auto generated code by adding handwritten code for such cases. Let's call this hybrid solution i.e. partially generated and partially handwritten. This manual explains how to write hybrid solution using Web Application Firewall Config as an example.
Examining auto generated code
WAF Config's JS code is generated into ApplicationGateways-webApplicationFirewallConfiguration._js
file. If all the parameters were added directly into generator configuration there would be some incorrect parameters processing code in create
and set
commands e.g. option disabled-rule-groups
assignment.
.option('--disabled-rule-groups [disabled-rule-groups]', $('the disabled rule groups'))
// ...
if (options.disabledRuleGroups) {
// This line is incorrect, see explanation below
parameters.webApplicationFirewallConfiguration.disabledRuleGroups = options.disabledRuleGroups;
}
According to API specifications, disabledRuleGroups is an array of objects. It wouldn't be user-friendly to ask user to pass those groups as a JSON string, so we need to find a better solution.
Writing hybrid solution
First of all, we need to remove disabled-rule-groups
option from generated commands (including related logic), this could be done during generation by removing of corresponding parameter from generator configuration. WAF config is an object accessible through applicationGateway
object, so it's easy to add custom objects to disabledRuleGroups
array. To do that we need to create a new subcategory of waf-config
that will control disabled rule groups.
WAF config is a part of network
service, it's core module code is located in network._js
. network._js
wouldn't be re-generated, so it is the place where new handwritten commands should be added. In our case, we create new categories structure in network._js
:
// appGateway is 'application-gateway' category, it was already created
var webApplicationFirewallConfiguration = appGateway.category('waf-config')
.description($('Commands to manage web application firewall configuration'));
var disabledRuleGroups = webApplicationFirewallConfiguration.category('disabled-rule-groups')
.description($('Commands to manage disabled rule groups'));
Now we write our own command in disabled-rule-groups
category. The following code is create
command that adds new disabled rule group to disabledRuleGroups
array in WAF config. It's options are resource group name and gateway name (required to get application gateway that contains WAF Config itself), rule group name and comma-separated list of rule IDs. Most of this code can be copy-pasted from other commands.
disabledRuleGroups.command('create [resource-group] [gateway-name] [rule-group-name]')
.description($('Create a group of disabled rules'))
.usage('[options] <resource-group> <gateway-name> <rule-group-name>')
.option('-g, --resource-group <resource-group>', $('the name of the resource group'))
.option('-w, --gateway-name <gateway-name>', $('the gateway name'))
.option('-n, --rule-group-name <rule-group-name>', $('the name of the rule group that will be disabled'))
.option('-r, --rules [rules]', $('The list of rules that will be disabled. If null, all rules of the rule group will be disabled'))
.execute(function (resourceGroup, gatewayName, ruleGroupName, options, _) {
resourceGroup = cli.interaction.promptIfNotGiven($('Resource group: '), resourceGroup, _);
gatewayName = cli.interaction.promptIfNotGiven($('Application gateway name: '), gatewayName, _);
options.ruleGroupName = cli.interaction.promptIfNotGiven($('rule group name: '), ruleGroupName, _);
var subscription = profile.current.getSubscription(options.subscription);
var networkManagementClient = utils.createNetworkManagementClient(subscription);
var appGateway = new AppGateway(cli, networkManagementClient);
appGateway.createDisabledRuleGroup(resourceGroup, gatewayName, options, _);
});
AppGateway is handwritten utility for working with application gateways that contains useful reusable code. Method createDisabledRuleGroup
gets application gateway object, finds WAF config, pushes rule group to disabledRuleGroups
array and sends updated config to the server through networkManagementClient
. With some omissions it looks like this.
appGateway = networkManagementClient.applicationGateways.get(resourceGroup, appGatewayName, null, _);
var wafConfig = appGateway.webApplicationFirewallConfiguration;
// ...
wafConfig.disabledRuleGroups.push({
ruleGroupName: options.ruleGroupName,
rules: options.rules ? options.rules.split(',').map(utils.parseInt) : null
});
// ...
networkManagementClient.applicationGateways.createOrUpdate(resourceGroup, appGatewayName, result, _);
Operations set
, show
, list
and delete
can be written the same way.
For additional information on developing custom commands please refer to azure-xplat-cli documentation (especially the part about writing cmdlets)
For additional information on extending CLI's codebase please refer to azure-xplat-cli documentation (especially the part about writing cmdlets)