Use Arrays and for-loops in BICEP - powershell

I am passing an array of services into the BICEP template
param objectarray array =[]
I also have an array variable that contains a subset of the service names that I passed in
var objectarray =
[
'Abc'
'DEF'
'GHI'
]
My intention is to see if the service names being passed in are in the subset and if so, then I need to set another variable accordingly. What's the best way to do that? I've used conditional loops and if statements in BICEP before but not together.
var isInSubset =
Additional Info
I did try this
var apis_with_listener = contains(objectarray, objectarray)
but the resource that I am creating is a backend policy and i am using a for loop in the resource creation and setting the value based on what apis_with_listener is set to. Sorry, this sounds confusing.
value: '<\r\n<policies>\r\n <inbound>\r\n <base />\r\n <set-backend-service backend-id="${APIM_Name}" sf-resolve-condition="#(context.LastError?.Reason == "BackendConnectionFailure")" sf-service-instance-name="#("fabric:/${Application_Name}${Customer_AreaName}/${api}")" **sf-listener-name="${api}Service**" />\r\n </inbound>\r\n <backend>\r\n <base />\r\n </backend>\r\n <outbound>\r\n <base />\r\n </outbound>\r\n <on-error>\r\n <base />\r\n </on-error>\r\n</policies>'
so the sf-listener-name needs to be set ONLY if the service is one of the ones in the subset. Not sure how to do a for loop and an if properly
resource BackendPolicy 'Microsoft.ApiManagement/service/apis/policies#2021-04-01-preview'= [for (api,i) in API_Names : {
name: 'policy'
parent:API_Service[i]
properties: {
value: '<\r\n<policies>\r\n <inbound>\r\n <base />\r\n <set-backend-service backend-id="${APIM_Name}" sf-resolve-condition="#(context.LastError?.Reason == "BackendConnectionFailure")" sf-service-instance-name="#("fabric:/${Application_Name}${Customer_AreaName}/${api}")" sf-listener-name="${api}Service" />\r\n </inbound>\r\n <backend>\r\n <base />\r\n </backend>\r\n <outbound>\r\n <base />\r\n </outbound>\r\n <on-error>\r\n <base />\r\n </on-error>\r\n</policies>'
format: 'xml'
}
}]

First of all, Bicep doesn't allow loops within loops, but you don't need that here as you're just looking for whether an array contains the string value you're matching against. Let's start my saying that all the APIM policy setting is going to happen in its own module, 'sf-apimPolicy.bicep', for clarity.
You indicated that you want to pass in an array of your Service Fabric service names and see if any of them are in a subset, presumably one kept in your intended module, so we'll start with that.
// sf-apimPolicy.bicep
#description('Your APIM resource name')
param APIM_Name string
#description('The prefix of your SF application name')
param Application_Name string
#description('Used to route to the appropriate SF application per customer')
param Customer_AreaName string
#description('Contains each of your Service names being passed in')
param API_Names array //Presumably this is the 'objectarray' you mention in the question
#description('The array of service names that set a constraint on what can be set in this policy')
param ApiNameConstraints array
resource ApimService 'Microsoft.ApiManagement/service/apis#2021-04-01-preview' existing = {
name: '${APIM_Name}/${ApiServiceName}'
}
//And finally you're looking to set the policy itself if ApiNameConstraints contains your api value
resource BackendPolicy 'Microsoft.ApiManagement/service/apis/policies#2021-04-01-preview' = [for (api, i) in API_Names: if (contains(ApiNameConstraints, api)): {
name: 'policy'
parent: ApimService
properties: {
value: '''
<policies>
<inbound>
<base> // As an aside, changed this from your original value as it used a content-less close tag
<set-backend-service backend-id="${APIM_Name}" sf-resolve-condition="#(context.LastError?.Reason == "BackendConnectionFailure")" sf-service-instance-name="#("fabric:/${Application_Name}${Customer_AreaName}/${api}")" **sf-listener-name="${api}Service**" />
</base> //Also changed this as it didn't use a close tag
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
'''
format: 'xml'
}
}]
Then merely call this module and populate the various parameters to call it and the loop will be handled internally.

Related

Karate: Replace text at Envelope level [duplicate]

I need to parse and print ns4:feature part. Karate prints it in json format. I tried referring to this answer. But, i get 'ERROR: 'Namespace for prefix 'xsi' has not been declared.' error, if used suggested xPath. i.e.,
* def list = $Test1/Envelope/Body/getPlan/planSummary/feature[1]
This is my XML: It contains lot many parts with different 'ns' values, but i have given here an extraxt.
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Header/>
<S:Body>
<ns9:getPlan xmlns:ns10="http://xmlschema.test.com/xsd_v8" xmlns:ns9="http://xmlschema.test.com/srv/SMO_v4" xmlns:ns8="http://xmlschema.test.com/xsd/Customer_v2" xmlns:ns7="http://xmlschema.test.com/xsd/Customer/Customer_v4" xmlns:ns6="http://schemas.test.com/eca/common_types_2_1" xmlns:ns5="http://xmlschema.test.com/xsd/Customer/BaseTypes_1_0" xmlns:ns4="http://xmlschema.test.com/xsd_v4" xmlns:ns3="http://xmlschema.test.com/xsd/Enterprise/BaseTypes/types/ping_v1" xmlns:ns2="http://xmlschema.test.com/xsd/common/exceptions/Exceptions_v1_0">
<ns9:planSummary xsi:type="ns4:Plan" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns5:code>XPBSMWAT</ns5:code>
<ns5:description>Test Plan</ns5:description>
<ns4:category xsi:nil="true"/>
<ns4:effectiveDate>2009-11-05</ns4:effectiveDate>
<ns4:sharingGroupList>
<ns4:sharingCode>CAD_DATA</ns4:sharingCode>
<ns4:contributingInd>true</ns4:contributingInd>
</ns4:sharingGroupList>
<ns4:feature>
<ns5:code>ABC</ns5:code>
<ns5:description>Service</ns5:description>
<ns5:descriptionFrench>Service</ns5:descriptionFrench>
<ns4:poolGroupId xsi:nil="true"/>
<ns4:switchCode/>
<ns4:type/>
<ns4:dtInd>false</ns4:dtInd>
<ns4:usageCharge>0.0</ns4:usageCharge>
<ns4:connectInd>false</ns4:connectInd>
</ns4:feature>
</ns9:planSummary>
</ns9:getPlan>
</S:Body>
</S:Envelope>
This is the xPath i used;
Note: I saved above xml in a separate file test1.xml. I am just reading it and parsing the value.
* def Test1 = read('classpath:PP1/data/test1.xml')
* def list = $Test1/Envelope/Body/*[local-name()='getPlan']/*[local-name()='planSummary']/*[local-name()='feature']/*
* print list
This is the response i am getting;
16:20:10.729 [ForkJoinPool-1-worker-1] INFO com.intuit.karate - [print] [
"ABC",
"Service",
"Service",
"",
"",
"",
"false",
"0.0",
"false"
]
How can i get the same in XML?
This is interesting, I haven't seen this before. The problem was you have an attribute with a namespace xsi:nil="true" which is causing problems when you take a sub-set of the XML but the namespace is not defined anymore. If you remove it first, things will work.
Try this:
* remove Test1 //poolGroupId/#nil
* def temp = $Test1/Envelope/Body/getPlan/planSummary/feature
Another approach you could have tried is to do a string replace to remove troublesome stuff in the XML before doing XPath.
EDIT: added info on how to do a string replace using Java. The below will strip out the entire xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns4:Plan" part.
* string temp = Test1
* string temp = temp.replaceAll("xmlns:xsi[^>]*", "")
* print temp
So you get the idea. Just use regex.
Also see: https://stackoverflow.com/a/50372295/143475

HTL AEM conditional OR operator issue

I want to be able to use the "||" OR operator in this context
inside a schema, i have two dates I need to pull from the JCR content if the value in the field has not been the author
<script type="application/ld+json" >
"datePublished": "${properties.datePublishedArticle #context="html" ||'yyyy-MM-dd' # format=currentPage.lastModified }",
"dateModified": "${properties.dateModifiedArticle # context="html" || 'yyyy-MM-dd' # format=currentPage.lastModified}"
</script>
org.apache.sling.api.scripting.ScriptEvaluationException:
mismatched input '#' expecting {'}', '.', 'in', '&&', '||', ',', '['} in line 67 where datepublished is located.
In order words, if author has not authored a value, it will take the value from the jcr content. They work fine when done separately.
Do not understand the error that is indicating.
Have you tried to use it like this:
<script type="application/ld+json" >
"datePublished": "${properties.datePublishedArticle ||'yyyy-MM-dd' # context="html", format = currentPage.lastModified }",
"dateModified": "${properties.dateModifiedArticle || 'yyyy-MM-dd' # context="html", format = currentPage.lastModified}"
</script>
I would think that the error is happening because you shouldn't be repeating the '#' block in the same HTL statement.
Although #atgar's solution does not throw an error, I think it does not do what the author asked for: output either the authored date or the JCR (lastModified) date.
Something like this would be closer to what he needs:
<script type="application/ld+json" >
"datePublished": "${'yyyy-MM-dd' # context='html', format = (properties.datePublishedArticle ? properties.datePublishedArticle : currentPage.lastModified) }",
"dateModified": "${'yyyy-MM-dd' # context='html', format = (properties.dateModifiedArticle ? properties.dateModifiedArticle : currentPage.lastModified)}"
</script>
Please note, when using HTL date formatting, the actual values should be in the format option.

Cake PeekXml does not "ignore" namespace

Is it a bug or per design that xmlns attribute is not ignored?
(cake version 0.33.0)
With an Xml like so (a too simplified nuspec file):
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<!-- Continuously updated elements -->
<version>3.0.0</version>
</metadata>
</package>
I do a naÏve call
var x = XmlPeek( "my.nuspec", "/package/metadata/version/text()" );
ad get the result x==null.
So I specify the namespace like so:
var settings = new XmlPeekSettings{
Namespaces = new Dictionary<string, string> {{
"ps", "http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"
}}
};
var x = XmlPeek( "my.nuspec", "/ps:package/ps:metadata/ps:version/text()", settings);
and get the result x==3.0.0 I anticipated.
It is not a bug.
To ignore the namespace you can use namespace agnostic xpath such as local-name():
var x = XmlPeek( "my.nuspec", "/*[local-name() = 'package']/*[local-name() = 'metadata']/*[local-name() = 'version']/text()");
or if you have only one version node:
var x = XmlPeek( "my.nuspec", "//*[local-name()='version']/text()");
but be careful with documents with large numbers of elements - this can become very slow.

How do I pass Variants as props to React Components in ReasonML?

I have tried the following approach to be able to send variants as props.
type ipAddr =
| IPV4
| IPV8;
[#react.component]
let make = () => {
let appData = Data.tileData;
<div className="App">
<header className="flex outline justify-between" />
<Content selected={ipAddr.IPV4} appData />
</div>;
};
But it throws the error,
ninja: error: rebuilding 'build.ninja': subcommand failed
I have tried sending variants directly to Component as well.
<div className="App">
<header className="flex outline justify-between" />
<Content selected=IPV4 appData />
</div>;
But it ended up returning another error
Start compiling ninja: error: dependency cycle: src/App-ReactHooksTemplate.cmj -> src/Content-ReactHooksTemplate.cmj
-> src/App-ReactHooksTemplate.cmj
Finish compiling(exit: 1)
Where am I going wrong?
DISCLAIMER: I don't know ReasonML, however
If it was OCaml, you'd just write IPV4, no need to qualify it like ipAddr.IPV4.
Perhaps this is the same in Reason?
I have solved this another way. Instead of passing the variant as prop, I simply rendered different components based on variant value.
[#react.component]
let make = () => {
let appData = Data.tileData;
switch (selected) {
| IPV4 =>
<div>
<IPV4Renderer appData />
</div>
| IPV6 =>
<div>
<IPV6Renderer appData />
</div>
};
};

How to send email to recipient with umlauts in domain name?

In my app I have to send email to recipient who has umlauts in domain name.
Example:
"test#äöü.test.com"
I'm using cfmail tag and I'm getting such error:
"invalid definition for attribute to at tag mail"
"Invalid E-Mail Address definition (test#äöü.test.com)"
Is there any way to send email to such recipients in coldfusion?
There is even a easier solution! Why not use Oracles built in class:
http://download.oracle.com/javase/6/docs/api/java/net/IDN.html#toUnicode(java.lang.String)
Then you only have to do this (example shows from punycode to Unicode):
<cfset strUrl = "xn--land-poa.se" />
<!--- Create a Java URL. --->
<cfset jUrl = CreateObject( "java", "java.net.IDN" ).toUnicode(strUrl) />
<cfoutput>
#jUrl#
You don´t have to download anything!
I'm no I18N expert but I was intrigued enough to investigate and come up with the following solution.
The problem is essentially how to send mail to Internationalised Domain Names (IDN), i.e. those which contain non-ASCII characters. IDNs are valid nowadays but not recognized by many systems including Java (and therefore ColdFusion, which uses the Java validation for CFMAIL address fields - hence the error you're seeing).
For a system to recognise an IDN it needs to be converted to an ASCII form called Punycode. For example müller.org needs to be converted to xn--mller-kva.org
LibIdn is an OS java library that will do this and the following code shows how you can hook it up to CF using Mark Mandel's JavaLoader.
<cffunction name="convertIdnToAscii" returntype="string" output="false">
<cfargument name="domain" type="string" required="true">
<cfscript>
var local = {};
// these paths assume the JavaLoader folder and the libidn-1.22.jar are in the same folder as the cfm template.
local.javaLoaderPath = "javaLoader.JavaLoader";
local.idnLibPath = ExpandPath( "libidn-1.22.jar" );
// convert the IDN lib path to an array which is what JavaLoader expects
local.libPathArray = [ local.idnLibPath ];
//load the IDN Lib
loader = CreateObject( "component",local.javaLoaderPath ).init( local.libPathArray );
// create an instance of the IDN lib
local.idn = loader.create( "gnu.inet.encoding.IDNA" ).init();
// convert the domain name
return local.idn.toASCII( arguments.domain );
</cfscript>
</cffunction>
<cffunction name="convertIdnAddress" returntype="string" output="false">
<cfargument name="address" type="string" required="true">
<cfscript>
var local = {};
local.domain = GetToken( arguments.address,2,"#" );
local.converted = convertIdnToAscii( local.domain );
return Replace( arguments.address,local.domain,local.converted );
</cfscript>
</cffunction>
<!--- Loop over a list of addresses and convert them if necessary --->
<cfset processedAddresses = []>
<cfloop list="test#äöü.test.com,test#example.com" index="address">
<cfif( NOT IsValid( "email",address ) )>
<cfset address = convertIdnAddress( address )>
</cfif>
<cfmail server="0.0.0.0" from="sender#mydomain.com" to="#address#" subject="test">Message</cfmail>
<cfset ArrayAppend( processedAddresses,address )>
</cfloop>
<cfdump var="#processedAddresses#">
This will send 2 emails (to a non-existent mailserver) and dump the converted addresses:
test#xn--4ca0bs.test.com
test#example.com
Notes:
To get the libidn jar file, download and extract the tar and look for it in the Java directory
The above assumes the libidn jar and JavaLoader package are located in the same folder as the template contain the CF code
The above should work on CF8 and above, although I've only tested it on CF9.
Be aware there's no error handling for addresses that might be invalid for reasons other than it containing an IDN.