ESB WSO2 - Using ARRAY as paramType in a datasource query - wso2

I want to sent a request to my api like this:
{
"id": "22",
"code": "AXB23, ADR40, SMRS2"
}
And I want to use every code in a query like "SELECT ... WHERE ... IN(AXB23, ADR40, SMRS2)" in my datasource but I don't know how.
I found here a solution with <param type="IN" name="code" paramType="ARRAY" sqlType="STRING"/>
but it doesn't work.
Any suggestions?
EDIT*:
This is my api (inSequence - forget about the id from request in the original post, I just only have that 'code')
<inSequence>
<property expression="json-eval($..code)" name="code" scope="default" type="STRING"/>
<payloadFactory media-type="xml">
<format>
<selectTEST xmlns:xs="http://ws.wso2.org/dataservice/selectTEST">
<code>$1</code>
</selectTEST>
</format>
<args>
<arg evaluator="xml" expression="$ctx:code"/>
</args>
</payloadFactory>
<property name="messageType" scope="axis2" type="STRING" value="text/xml"/>
<header name="Action" scope="default" value="selectTEST"/>
<call>
<endpoint key="ep.select"/>
</call>
<property name="messageType" scope="axis2" type="STRING" value="application/json"/>
<respond/>
</inSequence>
and this is my dataservice:
<operation name="selectTEST" returnRequestStatus="true">
<call-query href="selectTEST">
<with-param name="code" query-param="code"/>
</call-query>
</operation>
<query id="selectTEST" useConfig="default">
<sql><![CDATA[
select x, y, z, w from mydatabase where code in (:code)
]]></sql>
<param type="IN" name="code" paramType="ARRAY" sqlType="STRING"/>
<result outputType="json">
{
"data": {
"records": [
{
"x": "$x",
"y": "$y",
"z": "$z",
"w": "$w"
}
]
}
}
</result>
</query>
My request:
{
"code": ["AXB23", "ADR40"]
}

If you are using the array type parameters you should pass the values as a proper JSON array. Something like this will work for you.
The Request:
{
"_poststudent": {
"code": ["AXB23","ADR40"]
}
}
Dataservice Query
<query id="CreateStudents" useConfig="default">
<sql>SELECT id, name, school, grade FROM students WHERE grade IN (:code)</sql>
<param name="code" paramType="ARRAY" sqlType="STRING" type="IN" optional="false" />
<result outputType="json">{"students":{"student":[{"name":"$name","school":"$school","grade":"$grade"}]}}{"students":{"student":[{"name":"$name","school":"$school","grade":"$grade"}]}}{"students":{"student":[{"name":"$name","school":"$school","grade":"$grade"}]}}{"students":{"student":[{"name":"$name","school":"$school", "grade":"$grade"}]}}{"students":{"student":[{"name":"$name","school":"$school","grade":"$grade"}]}}{"students":{"student":[{"name":"$name","school":"$school","grade":"$grade"}]}}{"students":{"student":[{"name":"$name","school":"$school", "grade":"$grade"} ] }}{"students":{"student":[{"name":"$name","school":"$school","grade":"$grade"} ] } }</result>
</query>
Update
The Full Dataservice config.
<data name="RESTDataService" serviceNamespace="http://ws.wso2.org/dataservice/samples/json_sample" transports="http https">
<description>Exposing the data service as a REST service.</description>
<config id="default">
<property name="driverClassName">com.mysql.jdbc.Driver</property>
<property name="url">jdbc:mysql://localhost:3306/school_db</property>
<property name="org.wso2.ws.dataservice.user">xxxxx</property>
<property name="org.wso2.ws.dataservice.password">xxxxx</property>
</config>
<resource method="POST" path="student">
<call-query href="ReadStudents">
<with-param name="code" query-param="code" />
</call-query>
</resource>
<operation name="selectTEST" returnRequestStatus="true">
<call-query href="ReadStudents">
<with-param name="code" query-param="code" />
</call-query>
</operation>
<query id="ReadStudents" useConfig="default">
<sql>SELECT id, name, school, grade FROM students WHERE grade IN (:code)</sql>
<param name="code" paramType="ARRAY" sqlType="STRING" type="IN" optional="false" />
<result outputType="json">{ "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } { "students":{ "student":[ { "name":"$name", "school":"$school", "grade":"$grade" } ] } } </result>
</query>
</data>
Note that I have specified a <resource method="POST" path="student">. resources are for RESTfull invocations, and <operation name="selectTEST" returnRequestStatus="true"> are for SOAP calls. I have created both RestFul and SOAP services for your reference. Following is how you can invoke them.
Note: Here I'm directly calling the Dataservice.
RestFull Invocation.
curl --location --request POST 'http://localhost:8290/services/RESTDataService/student' \
--header 'Content-Type: application/json' \
--data-raw '{
"students": {
"code": ["AXB23", "ADR40"]
}
}'
SOAP Service Call
curl --location --request POST 'http://localhost:8290/services/RESTDataService/selectTEST' \
--header 'Action: selectTEST' \
--header 'Content-Type: text/xml' \
--header 'Accept: application/json' \
--data-raw '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope/">
<soap:Body>
<code>AXB23</code>
<code>ADR40</code>
</soap:Body>
</soap:Envelope>'
Note: In a XML message an Array is depicted as repeating elements as shown above.

Related

Merge Array object elements using comparison

We have a requirement where we need to feed data into an empty json array element from another array object using element comparison.
The sample payloads and required results are mentioned below for better understanding.
Payload1: (Input Payload)
{
"data": [
{
"name":"ram",
"eno":"100",
"dept":"Sales",
"sal":null
},
{
"name":"gopal",
"eno":"101",
"dept":"Sales",
"sal":null
},
{
"name":"hari",
"eno":"102",
"dept":"Sales",
"sal":null
},
{
"name":"pankaj",
"eno":"103",
"dept":"Sales",
"sal":null
},
{
"name":"raju",
"eno":"104",
"dept":"Sales",
"sal":null
}
]
}
Payload2: (Response From a third party webservice)
{
"data": [
{
"eno": "100",
"sal": 2000
},
{
"eno": "101",
"sal": 2300
},
{
"eno": "102",
"sal": 1800
},
{
"eno": "104",
"sal": 2500
}
]
}
Required Result:
{
"data": [
{
"name":"ram",
"eno":"100",
"dept":"Sales",
"sal":2000
},
{
"name":"gopal",
"eno":"101",
"dept":"Sales",
"sal":2300
},
{
"name":"hari",
"eno":"102",
"dept":"Sales",
"sal":1800
},
{
"name":"raju",
"eno":"104",
"dept":"Sales",
"sal":2500
}
]
}
.................................................................
Here is how you can do this by only using synapse. I hardcoded the second response.
<?xml version="1.0" encoding="UTF-8"?>
<api context="/HelloWorld" name="HelloWorld" xmlns="http://ws.apache.org/ns/synapse">
<resource methods="POST">
<inSequence>
<enrich>
<source clone="true" type="body"/>
<target property="inputBody" type="property"/>
</enrich>
<payloadFactory media-type="json">
<format>
{
"data": [
{
"eno": "100",
"sal": 2000
},
{
"eno": "101",
"sal": 2300
},
{
"eno": "102",
"sal": 1800
},
{
"eno": "104",
"sal": 2500
}
]
}
</format>
<args/>
</payloadFactory>
<foreach expression="json-eval($.data)" id="foreach_1">
<sequence>
<property expression="json-eval($.eno)" name="eno" scope="default" type="STRING"/>
<property expression="json-eval($.sal)" name="sal" scope="default" type="STRING"/>
<enrich>
<source clone="true" property="inputBody" type="property"/>
<target type="body"/>
</enrich>
<enrich>
<source clone="true" property="sal" type="property"/>
<target xpath="//data[eno=$ctx:eno]/sal"/>
</enrich>
<enrich>
<source clone="true" type="body"/>
<target property="inputBody" type="property"/>
</enrich>
</sequence>
</foreach>
<enrich>
<source clone="true" property="inputBody" type="property"/>
<target type="body"/>
</enrich>
<property name="messageType" scope="axis2" type="STRING" value="application/json"/>
<respond/>
</inSequence>
<outSequence/>
<faultSequence/>
</resource>
</api>

WSO2 Converts embedded XML to JSON in Payload

I embed an XML into a JSON for the field details and wso2 converts it to JSON. How can I resolve this?
<payloadFactory media-type="json">
<format>{
"Customer": {
"name": "$1",
"details": "$2"
}
}</format>
<args>
<arg evaluator="xml" expression="get-property('name')"/>
<arg evaluator="xml" expression="get-property('detail')"/>
</args>
</payloadFactory>
Output
{
"Customer": {
"name": "Adam",
"details": "{"id": "123"}"
}
}
expected
{
"Customer": {
"name": "Adam",
"details": "<id>123</id>"
}
}
You can simply add the flag literal="true" in the PLFactory args.
<payloadFactory media-type="json">
<format>{
"Customer": {
"name": "$1",
"details": "$2"
}
}</format>
<args>
<arg evaluator="xml" expression="get-property('name')"/>
<arg evaluator="xml" expression="get-property('detail') literal="true" />
</args>
</payloadFactory>

steeltoe serilog dynamic logger not working after migrating to steeltoe 3.0

I was using steeltoe 2.4.3 in my .net core 3.1 application. Recently I updated steeltoe packages to v3.0.1 and it looks like logs are not coming in serilog format. I am not able to figure out what is wrong. I do have the serilog configuration in my appsettings like below.
{
"management": {
"endpoints": {
"path": "/myexample/cloudfoundryapplication",
"cloudfoundry": {
"validateCertificates": false
},
"actuator": {
"exposure": {
"include": [ "*" ],
"exclude": [ "env", "refresh" ]
}
}
}
},
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Information",
"Steeltoe": "Information"
}
},
"WriteTo": [
{
"Name": "Console",
"Args": {
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
},
{
"Name": "Trace",
"Args": {
"formatter": "Serilog.Formatting.Compact.CompactJsonFormatter, Serilog.Formatting.Compact"
}
}
],
"Enrich": [ "FromLogContext" ]
},
"AllowedHosts": "*"
}
Program.cs is like below
public static class Program
{
public static void Main(string[] args)
{
Activity.DefaultIdFormat = ActivityIdFormat.W3C;
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.AddCloudFoundryConfiguration()
.ConfigureLogging((builderContext, loggingBuilder) =>
{
// Add Serilog Dynamic Logger
loggingBuilder.AddDynamicSerilog();
})
.AddCloudFoundryActuators()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
My .csproj looks iike below.
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<None Update="wwwroot\**\*;*.yml">
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Serilog.Sinks.Trace" Version="2.1.0" />
<PackageReference Include="Serilog.Formatting.Compact" Version="1.1.0" />
<PackageReference Include="Steeltoe.Extensions.Logging.DynamicSerilogCore" Version="3.0.1" />
<PackageReference Include="Steeltoe.Management.CloudFoundryCore" Version="3.0.1" />
<PackageReference Include="Steeltoe.Extensions.Configuration.CloudFoundryCore" Version="3.0.1" />
</ItemGroup>
</Project>
AddDynamicSerilog doesn't (currently) add a console sink, try updating your code to look like this:
AddDynamicSerilog(new LoggerConfiguration().WriteTo.Console())
This PR will result in the Console sink being added automatically if no configuration is provided, and it will probably ship in Steeltoe 3.0.2 (no ETA yet)

select and combine 2 arrays in xslt

I have a Input json below
[
{
"fileName": "abc",
"value": [
{
"SSC": {
"Payload": [
{
"Ledger": [
{
"Line": [
{
"AccountingCode": [
"402050046767"
],
"AccountingPeriod": [
"00720201243"
]
},
{
"AccountingCode": [
"203010334567"
],
"AccountingPeriod": [
"00720201234"
]
}
]
}
]
}
]
}
}
]
},
{
"fileName": "abc",
"value": [
{
"SSC": {
"Payload": [
{
"Ledger": [
{
"Line": [
{
"AccountingCode": [
"40205004"
],
"AccountingPeriod": [
"0072020"
]
},
{
"AccountingCode": [
"20301033"
],
"AccountingPeriod": [
"0072020"
]
}
]
}
]
}
]
}
}
]
}
]
my expected output is
[
{
"fileName": "abc",
"value": [
{
"SSC": {
"Payload": [
{
"Ledger": [
{
"Line": [
{
"AccountingCode": [
"402050046767"
],
"AccountingPeriod": [
"00720201243"
]
},
{
"AccountingCode": [
"203010334567"
],
"AccountingPeriod": [
"00720201234"
]
},
{
"AccountingCode": [
"40205004"
],
"AccountingPeriod": [
"0072020"
]
},
{
"AccountingCode": [
"20301033"
],
"AccountingPeriod": [
"0072020"
]
}
]
}
]
}
]
}
}
]
}
]
Below is the xslt i have tried but not getting the proper output
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="3.0"
xmlns="http://www.w3.org/2005/xpath-functions"
xpath-default-namespace="http://www.w3.org/2005/xpath-functions"
expand-text="yes">
<xsl:param name="input"/>
<xsl:output method="text"/>
<xsl:template name="xsl:initial-template">
<xsl:variable name="input-as-xml" select="json-to-xml($input)"/>
<xsl:variable name="transformed-xml" as="element(array)">
<array>
<map>
<string key="fileNames">
<xsl:for-each select="$input-as-xml//string[#key='fileName']">
<xsl:variable name="file" select="../string[#key='fileName']"/>
<xsl:value-of select="$file"/>
</xsl:for-each>
</string>
<array key="value">
<map>
<array key="SSC">
<map>
<array key="Payload">
<map>
<array key="Ledger">
<xsl:for-each select="$input-as-xml//array[#key='Ledger']">
<map>
<xsl:choose>
<xsl:when test="$input-as-xml//string[#key='fileName'] = 'abc'">
<xsl:value-of select="$input-as-xml//array[#key='Lines']"/>
</xsl:when>
</xsl:choose>
</map>
</xsl:for-each>
</array>
</map>
</array>
</map>
</array>
</map>
</array>
</map>
</array>
</xsl:variable>
<xsl:value-of select="xml-to-json($transformed-xml)"/>
</xsl:template>
</xsl:stylesheet>
I am tring to select Array "Lines" from the json if the file name is abc then combine the value of the selected in Array "Ledger"
Please can anyone help me understand how to achieve this in xslt.
You could do this without converting to XML and back again: just manipulate the JSON as a structure of maps and arrays. Something along the lines of
<xsl:template name="xsl:initial-template">
<xsl:variable name="in" select="json-doc('input.json')"/>
<xsl:variable name="out" as="map(*)*">
<xsl:for-each-group select="$in?*" group-by="?fileName">
<xsl:map>
<xsl:map-entry key="'fileName'" select="current-grouping-key()"/>
<xsl:map-entry key="'value'" select="
array {
map {'SSC':
map {'Payload':
array {
map {'Ledger':
array {
map{'Line': array:join(current-group()?
value?*?SSC?Payload?*?Ledger?*?Line)}
}
}
}
}"/>
</xsl:map>
</xsl:for-each-group>
</xsl:variable>
<xsl:sequence select="array{$out}"/>
</xsl:template>
I haven't tested this and there are probably a few bugs in the complex path to iron out, but I hope it conveys the idea.
(Note, this would be easier if XSLT 3.0 had an xsl:array instruction. If you're prepared to use Saxon extensions, there's a saxon:array that plugs this gap.)

CfChart Stacked bars and unstacked Lines

I am trying to create a CFChart with Stacked Bars and unstacked Lines. Have been trying to work around in the Webcharts Tool but no luck so far.
In the example image all the series are seriesplacement=stacked, but I want to have the bars(Avg and TDD) as stacked and the lines (Max and Min) as seriesplacement=default. Is there a way to achieve the same in Cfchart?
If I am understanding correctly, just set the placement of the line series in your xml style:
<?xml version="1.0" encoding="UTF-8"?>
<frameChart>
<elements place="Stacked" drawOutline="false">
<series index="0" place="Default" shape="Line" />
<series index="1" place="Default" shape="Line" />
</elements>
</frameChart>
Then generate a "stacked" chart as usual:
<cfchart format="png" style="#yourCustomStyle#">
<cfchartseries type="line" serieslabel="Min">
...
</cfchartseries>
<cfchartseries type="line" serieslabel="Max">
...
</cfchartseries>
<cfchartseries type="bar" serieslabel="Avg" >
...
</cfchartseries>
<cfchartseries type="bar" serieslabel="TDD">
...
</cfchartseries>
</cfchart>
CFChart is based on ZingCharts which is formatted using JSON. They have some good docs and a builder to develop with. I haven't figured out how to get the left y-scale to adjust to MIN value, MAX seems to be automatic. Below I put together some sample data and code (.cfm & .json) for you to get started with.
Some starter data and the CFChart info (Lines-StackedBars.cfm)
<cfscript>
GraphData = queryNew("");
queryAddColumn(GraphData, "dates", "Integer", ['1/1/11','1/2/11','1/3/11','1/4/11','1/5/11']);
queryAddColumn(GraphData, "MIN", "Integer", [42,37,45,29,61]);
queryAddColumn(GraphData, "MAX", "Integer", [110,98,82,103,94]);
queryAddColumn(GraphData, "AVG", "Integer", [80,75,80,65,85]);
queryAddColumn(GraphData, "TDD", "Integer", [23,33,32,28,26]);
</cfscript>
<cfchart chartwidth="800"
chartheight="500"
format="html"
style="LineStackedBar.json"
title="Unstacked Lines w/ Stacked Bars">
<cfchartseries query="GraphData"
type="Bar"
itemcolumn="dates"
valuecolumn="AVG"
color="blue"
serieslabel="AVG">
<cfchartseries query="GraphData"
type="Bar"
itemcolumn="dates"
valuecolumn="TDD"
color="green"
serieslabel="TDD">
<cfchartseries query="GraphData"
type="Line"
itemcolumn="dates"
valuecolumn="MIN"
color="red"
serieslabel="MIN">
<cfchartseries query="GraphData"
type="Line"
itemcolumn="dates"
valuecolumn="MAX"
color="orange"
serieslabel="MAX">
</cfchart>
The code above uses LineStackedBar.json to handle the formatting
{
"graphset":[
{
"legend":{
"position":"30%, 0%",
"border-color":"#CCCCCC",
"background-color":"#FFFFFF",
"margin-top":40,
"layout":"x4",
"shadow":false,
"alpha":1
},
"border-color":"#cccccc",
"tooltip":{
"font-size":11,
"font-color":"#FFFFFF",
"bold":true,
"font-family":"Helvetica",
"padding":5
},
"series":[
{
"hover-state":{
"visible":false
},
"shadow-blur-y":1,
"shadow-color":"#cccccc",
"shadow-alpha":1,
"shadow":true,
"background-color-2":"#FFCF8C",
"background-color":"#735328",
"type":"bar",
"stacked":"true",
"shadow-distance":2,
"shadow-blur-x":2
},
{
"hover-state":{
"visible":false
},
"shadow-blur-y":1,
"shadow-color":"#cccccc",
"shadow-alpha":1,
"shadow":true,
"background-color-2":"#FEFFD1",
"background-color":"#9D9C5D",
"type":"bar",
"stacked":"true",
"shadow-distance":2,
"shadow-blur-x":2
},
{
"hover-state":{
"visible":false
},
"line-color":"#699EBF",
"border-color":"#699EBF",
"line-width":3,
"type":"line",
"scales":"scale-x,scale-y-2",
"stacked":"false"
},
{
"hover-state":{
"visible":false
},
"line-color":"#143F59",
"border-color":"#143F59",
"line-width":3,
"type":"line",
"scales":"scale-x,scale-y-2",
"stacked":"false"
}
],
"scale-y":{
"tick":{
"line-gap-size":0,
"line-color":"#cccccc",
"line-width":1,
"size":10
},
"font-size":16,
"line-color":"#cccccc",
"bold":true,
"format":"%v",
"item":{
"font-size":11,
"font-family":"Helvetica",
"color":"#333333"
},
"label":{
"color":"#333333"
},
"line-width":2,
"font-family":"Helvetica",
"color":"#333333"
},
"scale-y-2":{
"tick":{
"line-gap-size":0,
"line-color":"#cccccc",
"line-width":1,
"size":10
},
"font-size":16,
"line-color":"#cccccc",
"bold":true,
"format":"%v",
"item":{
"font-size":11,
"font-family":"Helvetica",
"color":"#333333"
},
"label":{
"color":"#333333"
},
"line-width":2,
"font-family":"Helvetica",
"color":"#333333"
},
"plotarea":{
"margin-top":80,
"margin-left":70,
"margin-right":30
},
"scale-x":{
"tick":{
"line-gap-size":0,
"line-color":"#cccccc",
"line-width":1,
"size":10
},
"font-size":16,
"line-color":"#cccccc",
"bold":true,
"item":{
"font-size":11,
"font-family":"Helvetica",
"color":"#333333"
},
"guide":{
"line-width":0
},
"label":{
"color":"#333333"
},
"line-width":2,
"font-family":"Helvetica",
"color":"#333333"
},
"3d-aspect":{
"true3d":false
},
"background-color":"white",
"border-width":1,
"plot":{
"hover-marker":{
"background-color":"#888888",
"size":3
},
"marker":{
"background-color":"#cccccc",
"size":3
},
"preview":true,
"tooltip-text":"%v"
},
"type":"mixed",
"title":{
"border-width":1,
"border-color":"#cccccc",
"background-color":"white",
"font-size":18,
"bold":true,
"font-family":"Helvetica",
"color":"#333333"
}
}
]
}