Flat file to json conversion using beanIo - maps

I am trying to parse a fixedlength flat file using beanIo to json
Code:
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Map;
import org.beanio.BeanIOConfigurationException;
import org.beanio.BeanReader;
import org.beanio.StreamFactory;
import org.junit.Test;
import com.google.gson.Gson;
public class EmployeeBeanIOHandlerTest {
#Test
public void testHandleEmployee() {
// mapping pattern file
String mappingPatternFile = "pattern-mapping.xml";
// data file (csv)
String objectFile = "employee.csv";
// stream name defined in pattern mapping file
String streamName = "empData";
Gson gson = new Gson();
BeanReader beanReader = null;
Reader reader = null;
StreamFactory factory = null;
InputStream in = null;
try {
System.out.println("## RESULT FOR " + objectFile + " ##");
// create a StreamFactory
factory = StreamFactory.newInstance();
// load the setting file
in = this.getClass().getClassLoader()
.getResourceAsStream(mappingPatternFile);
// get input stream reader of object file (data file)
reader = new InputStreamReader(this.getClass().getClassLoader()
.getResourceAsStream(objectFile));
// load input stream to stream factory
factory.load(in);
beanReader = factory.createReader(streamName, reader);
Map<?, ?> record = null;
while ((record = (Map<?, ?>) beanReader.read()) != null) {
System.out.println(beanReader.getRecordName() + ": "
+ gson.toJson(record));
}
} catch (BeanIOConfigurationException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
if (beanReader != null) {
beanReader.close();
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
However the output i see:
header: {"id":"Header","date":"01012013"}
emp: {"lastName":"Lilik","title":"Senior Developer","hireDate":"Oct 1, 2009
12:00:00 AM","salary":7500000,"firstName":"Robertus"}
emp: {"lastName":"Doe","title":"Architect","hireDate":"Jan 15, 2008 12:00:00 AM","salary":8000000,"firstName":"Jane"}
emp: {"lastName":"Anderson","title":"Manager","hireDate":"Mar 18, 2006 12:00:00 AM","salary":9000000,"firstName":"Jon"}
trailer: {"id":"Trailer","count":"3"}
Which generates separate json object for each record found.
Reference site:
http://www.sourcefreak.com/2013/06/painless-flat-file-parsing-with-beanio/
Below is my requirement:
I want to have a consolidated Json file.
In case of duplicate record it should form an json array.
I would appreciate a help on this.

This answer is based on the data and pattern-mapping.xml file found in the link provided by the OP.
Data:
Header,01012013
Robertus,Lilik,Senior Developer,"75,000,00",10012009
Jane,Doe,Architect,"80,000,00",01152008
Jon,Anderson,Manager,"90,000,00",03182006
Footer,3
Mapping File:
This is the modified pattern-mapping.xml file. Note the use of a <group> element (myGroup) to encapsulate everything into a single group, which will force the BeanReader to read everything in one go. I also changed the maxOccurs to be 1 (one) for both the Header and the Footer records. Also, added the collection="list" attribute to theemp` record
<?xml version='1.0' encoding='UTF-8' ?>
<beanio xmlns="http://www.beanio.org/2012/03"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.beanio.org/2012/03 http://www.beanio.org/2012/03/mapping.xsd">
<stream name="empData" format="csv">
<group name="myGroup" class="map">
<record name="header" class="map" ridLength="0-2" maxOccurs="1">
<field name="id" rid="true" maxOccurs="1" literal="Header" />
<field name="date" />
</record>
<record name="emp" class="map" ridLength="4-5" collection="list">
<field name="firstName" />
<field name="lastName" />
<field name="title" />
<field name="salary" type="java.math.BigDecimal" format="#,###,###,00" />
<field name="hireDate" type="java.util.Date" format="MMddyyyy" minOccurs="0" />
</record>
<record name="trailer" class="map" ridLength="2" maxOccurs="1">
<field name="id" />
<field name="count" />
</record>
</group>
</stream>
</beanio>
Using the test case supplied and the modified mapping file, we get this result (reformatted by me):
myGroup: {
"trailer": {
"count": "3",
"id": "Footer"
},
"header": {
"date": "01012013",
"id": "Header"
},
"emp": [
{
"firstName": "Robertus",
"lastName": "Lilik",
"hireDate": "Oct 1, 2009 12:00:00 AM",
"title": "Senior Developer",
"salary": 7500000
},
{
"firstName": "Jane",
"lastName": "Doe",
"hireDate": "Jan 15, 2008 12:00:00 AM",
"title": "Architect",
"salary": 8000000
},
{
"firstName": "Jon",
"lastName": "Anderson",
"hireDate": "Mar 18, 2006 12:00:00 AM",
"title": "Manager",
"salary": 9000000
}
]
}
Hope this helps

Related

How do i show embed image from api

"content": [
{
"type": "full_richtext",
"value": "<p data-block-key=\"a9kn6\">গেমটির কাহিনি গড়ে উঠেছে ইয়ারা নামের এক দেশকে কেন্দ্র করে। কাল্পনিক এ দেশটি শাসন করছে অ্যান্টন কাস্টিলো। নিষ্ঠুর এ শাসক সাধারণ মানুষের ওপর অত্যাচারের পাশাপাশি দেশ পরিচালনায়ও ব্যর্থ। আর তাই একসময়ের সমৃদ্ধ দেশটি অর্থনৈতিকভাবে এবং প্রযুক্তি ব্যবহারে অনেক পিছিয়ে পড়েছে। অত্যাচারী শাসকের কারণে প্রতিবেশী দেশগুলোও ইয়ারার সঙ্গে সম্পর্ক ত্যাগ করেছে। আর তাই অ্যান্টন কাস্টিলোর কাছ থেকে ইয়ারাকে মুক্ত করতে গেরিলাযুদ্ধে নামে ‘ডানি রোজাস’। এই বীর গেরিলাযোদ্ধার পরিচয়েও নিজেকে খুঁজে পাওয়া যাবে গেমটিতে।</p>",
"id": "c52035cd-c313-4e69-8123-98b2d2247228"
},
{
"type": "full_richtext",
"value": "<p data-block-key=\"a9kn6\"></p><embed alt=\"\" embedtype=\"image\" format=\"fullwidth\" id=\"4\"/><p data-block-key=\"6q2ur\"></p>",
"id": "516a6751-3788-44b9-a984-a81807baf455"
},
{
"type": "full_richtext",
"value": "<p data-block-key=\"a9kn6\">অ্যাকশননির্ভর গেমটির গ্রাফিকস খুবই উন্নত মানের। আর তাই গহিন জঙ্গলে গা ঢাকা দিয়ে অ্যান্টন কাস্টিলো বাহিনীর বিরুদ্ধে লড়াইয়ের সময় প্রতি পদে মিলবে যুদ্ধের আবহ। গেমটিতে তিনটি স্থানে গেরিলা হামলার সুযোগ মিলবে। তিনটি স্থানে হামলার সময় ভিন্ন পথে আলাদা চরিত্রের গেরিলা বাহিনীর মাধ্যমে শত্রুর বিরুদ্ধে লড়াই করতে হবে। এ সময় প্রধান ক্যাম্প পরিবর্তনের পাশাপাশি নতুন অস্ত্র, হামলার স্থান ও কৌশলে পরিবর্তন আনতে হওয়ায় প্রতিটি হামলার সময়ই মনে হবে নতুন গেম খেলছেন।</p><p data-block-key=\"4a3f3\"></p><p data-block-key=\"3j3go\">গেমটিতে গেরিলাযুদ্ধের সময় সহযোগী হিসেবে দেখা মিলবে বেশ কিছু চরিত্রের, যাদের মধ্যে জুয়ান কর্টেজ অন্যতম। যুদ্ধের ময়দানে আপনার মনোবল ঠিক রাখতে হাস্যকর বিভিন্ন ঘটনা নিয়মিত ঘটাবে মজার এ চরিত্র। এল টেগরের নাম না বললেই নয়, সাবেক এ গেরিলাযোদ্ধা অভিজ্ঞতা ও পরামর্শ দিয়ে যুদ্ধে জয়ী হতে ভূমিকা রাখবে। এ ছাড়া বিভিন্ন এলাকায় থাকা প্রধান ক্যাম্পে দেখা মিলবে বিভিন্ন চরিত্রের।<br/>ইউবিসফটের তৈরি গেমটি উইন্ডোজের পাশাপাশি প্লেস্টেশন ৪, প্লেস্টেশন ৫, এক্সবক্স ওয়ান, সিরিজ এক্স ও সিরিজ এস এবং গুগল স্ট্যাডিয়াতেও খেলা যাবে।<br/></p><h3 data-block-key=\"12s3k\"><b>কম্পিউটারে খেলতে কমপক্ষে যা লাগবে</b></h3><p data-block-key=\"1bcsg\">অপারেটিং সিস্টেম: উইন্ডোজ ১০<br/>প্রসেসর: ইন্টেল কোর আইসেভেন বা এমএমডি রাইজেন ফাইভ<br/>মেমোরি: ১৬ গিগাবাইট র‍্যাম<br/>গ্রাফিকস: ৮ গিগাবাইটের এএমডি আরএক্স ৪৬০ অথবা এনভিডিয়া জিফোর্স জিটিএক্স ৯৬০।<br/>জায়গা: ৬০ গিগাবাইট</p>",
"id": "7754100b-d91b-477c-92da-45cee0962dca"
}
],
I have this type of array from api.
I can showed the string data, but cannot show the embedded image.
I am using below code for shown the string from the api. Help me to find out the problem, Thanks to All
<div className='Details-news-content'>
{
newss.content.map((data, index) => (
<p key={index} dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(data.value) }}>
</p>))}
</div>
It's codded in Wagtail.
In wagtail API if the content's Richtext is not serialized the API will show wired type HTML format in API.
in this case, add this code in blocks.py
from wagtail.core import blocks
from wagtail.core.templatetags.wagtailcore_tags import richtext
class RichtextBlock(blocks.RichTextBlock):
def get_api_representation(self, value, context=None):
return richtext(value.source)
Source: https://learnwagtail.com/tutorials/headless-cms-serializing-richtext-blocks/

How To Implement Full Search in Case Type using Salesforce?

I need to build out a solution to create a search field on the new Case Type Data object in all 3 of the Level fields and populate based on selection.
Similar to SF Global Search I would like to type 2-3 characters in the text search field and it would find the matching text in the Level1-3 fields and when selected the Level 1-3 field would populate.
This is the apex class
public class PickListHandler {
#AuraEnabled
public static List<String> getLevel1(){
List<String> tempLst1 = new List<String>();
for(AggregateResult ar : [select Level_1__c,COUNT(id) from Case_Type_Data__c group by Level_1__c])
{
tempLst1.add(''+ar.get('Level_1__c'));
}
return tempLst1;
}
#AuraEnabled
public static List<String> getLevel2(string strName){
List<String> tempLst2 = new List<String>();
for(AggregateResult ar : [select Level_2__c,COUNT(id) from Case_Type_Data__c where Level_1__c=:strName group by Level_2__c])
{
tempLst2.add(''+ar.get('Level_2__c'));
}
return tempLst2;
}
#AuraEnabled
public static List<String> getLevel3(string strName1,string strName2){
List<String> tempLst3 = new List<String>();
for(AggregateResult ar : [select Level_3__c,COUNT(id) from Case_Type_Data__c where Level_1__c=:strName1 and Level_2__c=:strName2 group by Level_3__c])
{
tempLst3.add(''+ar.get('Level_3__c'));
}
return tempLst3;
}
#AuraEnabled
public static String savecasetype(string level1,string level2,string level3,string id){
string strMsg='successfull';
try{
ERT_Case_Type__c obj=new ERT_Case_Type__c();
Obj.Case__c = id;
System.debug('CASE = '+ Obj.Case__c);
Obj.Level_1__c=level1;
System.debug('Level1 = '+ Obj.Level_1__c);
Obj.Level_2__c=level2;
System.debug('Level2 = '+ Obj.Level_2__c);
Obj.Level_3__c=level3;
System.debug('Level3 = '+ Obj.Level_3__c);
Insert obj;
}
catch(Exception ex){
strMsg='error';
}
return strMsg;
}
}
This is the Picklist handler component
<aura:component controller="PickListHandler" implements="force:appHostable,flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,force:hasRecordId,forceCommunity:availableForAllPageTypes,force:lightningQuickAction" access="global" >
<!-- Actions-->
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<!-- variable-->
<aura:attribute name="lstLevel1" type="String[]" />
<aura:attribute name="lstLevel2" type="String[]" />
<aura:attribute name="lstL3" type="String[]" />
<span> Level 1</span>
<ui:inputSelect aura:id="ddLevel1" change="{!c.getLvl1}">
<ui:inputSelectOption label="-Select-" value="true"/>
<aura:iteration items="{!v.lstLevel1}" var="value">
<ui:inputSelectOption label="{!value}" text="{!value}" />
</aura:iteration>
</ui:inputSelect>
<span>Level 2</span>
<ui:inputSelect aura:id="ddLevel2" change="{!c.getSelectedValue}">
<ui:inputSelectOption label="-Select-" value="true"/>
<aura:iteration items="{!v.lstLevel2}" var="value">
<ui:inputSelectOption label="{!value}" text="{!value}" />
</aura:iteration>
</ui:inputSelect>
<span>Level 3</span>
<ui:inputSelect aura:id="ddLevel3" >
<ui:inputSelectOption label="-Select-" value="true"/>
<aura:iteration items="{!v.lstL3}" var="value">
<ui:inputSelectOption label="{!value}" text="{!value}" />
</aura:iteration>
</ui:inputSelect>
<lightning:button variant="brand" label="Save" onclick="{!c.onConfirm}" />
</aura:component>
Regards,
Carolyn
You're asking for a lot, we wouldn't have your custom object. And this is old code, ui:inputSelect is deprecated for 1 year now. I'll try to help a bit but the whole thing needs your work too. And examples we can reproduce easily.
I'm going to cheat and use Philippe Ozil's ready component for the lookup/autocomplete thing.
It means you'd have to save LookupSearchResult class, the whole aura component and 2 aura events in your org before reading below. That's some prep work but it's battle-tested :)
Apex class
public with sharing class Stack64129038 {
#AuraEnabled(cacheable=true)
public static List<LookupSearchResult> search(String searchTerm, List<String> selectedIds){
if(String.isBlank(searchTerm) || searchTerm.length() < 2){
return null;
}
String t = '%' + searchTerm + '%'; // decide how you want to search, "starts with", "includes" or what
List<Case_Type_Data__c> records = [SELECT Id, Name, Level_1__c, Level_2__c, Level_3__c
FROM Case_Type_Data__c
WHERE Level_1__c LIKE :t OR Level_2__c LIKE :t OR Level_3__c LIKE :t
ORDER BY Level_1__c, Level_2__c, Level_3__c
LIMIT 20];
/* You could also experiment with SOSL?
records = [FIND :('*' + searchTerm + '*') IN ALL FIELDS
RETURNING Case_Type_Data__c(Id, Name, Level_1__c, Level_2__c, Level_3__c)][0];
*/
List<LookupSearchResult> results = new List<LookupSearchResult>();
for(Case_Type_Data__c ctd : records){
results.add(new LookupSearchResult(ctd.Id, 'Case_Type_Data__c', 'standard:case_wrap_up', ctd.Name,
String.join(new List<String>{ctd.Level_1__c , ctd.Level_2__c, ctd.Level_3__c}, '; ')
));
}
return results;
}
}
Aura component (html part)
<aura:component implements="force:hasRecordId,force:appHostable,flexipage:availableForAllPageTypes,force:lightningQuickAction" access="global" controller="Stack64129038">
<aura:attribute access="private" type="List" name="selection" default="[]"/>
<aura:attribute access="private" type="List" name="errors" default="[]"/>
<lightning:card title="New Case Type">
<lightning:recordEditForm aura:id="myForm" objectApiName="ERT_Case_Type__c" onsubmit="{!c.onSubmit}" onsuccess="{!c.onSuccess}">
<lightning:messages />
<c:Lookup selection="{!v.selection}" onSearch="{!c.lookupSearch}" onSelection="{!c.useSelected}" errors="{!v.errors}" label="Search" placeholder="Search Case Types Data"/>
<lightning:inputField aura:id="Level_1__c" fieldName="Level_1__c" />
<lightning:inputField aura:id="Level_2__c" fieldName="Level_2__c" />
<lightning:inputField aura:id="Level_3__c" fieldName="Level_3__c" />
<lightning:button class="slds-m-top_small" variant="brand" type="submit" name="save" label="Save" />
</lightning:recordEditForm>
</lightning:card>
</aura:component>
Aura component - JS controller part
({
lookupSearch : function(component, event, helper) {
// Get the lookup component that fired the search event
const lookupComponent = event.getSource();
const serverSearchAction = component.get('c.search');
lookupComponent.search(serverSearchAction);
},
useSelected: function(component, event, helper) {
const selection = component.get('v.selection');
const errors = component.get('v.errors');
if (selection.length) {
if(errors.length){ // Clear errors, if any
component.set('v.errors', []);
}
let levels = selection[0].subtitle.split('; ');
component.find('Level_1__c').set('v.value', levels[0]);
component.find('Level_2__c').set('v.value', levels[1]);
component.find('Level_3__c').set('v.value', levels[2]);
}
},
onSubmit: function(component, event, helper) {
debugger;
event.preventDefault(); // stop the form from submitting
var fields = event.getParam('fields');
fields.Case__c = component.get('v.recordId'); // link to "this" Case
component.find('myForm').submit(fields);
},
onSuccess: function(component, event, helper){
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"title": "Success!",
"message": "Case Type saved OK, refreshing",
"type": "success"
});
toastEvent.fire();
$A.get('e.force:refreshView').fire(); // reload page
}
})

How can I import all (mp3) files from a particular folder into an array in react.js?

I'm building an mp3 player with react.js and the HTML5 web audio API. I'm new to react (circa 2 weeks) but have 3 years experience with JavaScript.
It seems that in order for the mp3 files to play/load into the audio tag (within a react environment using the cmd line and localhost) I need to import them to the application(rather than just pointing to their URL in the src of the audio tag). See my question here
So at the moment I am importing the sounds in a hard-coded fashion as follows:
import React, { Component } from 'react';
import './music-player.css';
import mp3_file0 from './sounds/0010_beat_egyptian.mp3';
import mp3_file1 from './sounds/0011_beat_euphoric.mp3';
import mp3_file2 from './sounds/0014_beat_latin.mp3';
import mp3_file3 from './sounds/0015_beat_pop.mp3';
import mp3_file4 from './sounds/0027_falling_cute.mp3';
import mp3_file5 from './sounds/0028_feather.mp3';
import mp3_file6 from './sounds/0036_lose_cute.mp3';
import mp3_file7 from './sounds/0039_pium.mp3';
var mp3s = [];
mp3s[0] = mp3_file0;
mp3s[1] = mp3_file1;
mp3s[2] = mp3_file2;
mp3s[3] = mp3_file3;
mp3s[4] = mp3_file4;
mp3s[5] = mp3_file5;
mp3s[6] = mp3_file6;
mp3s[7] = mp3_file7;
const AudioPlayer = function(props) {
var mp3Src = mp3s[props.currentSoundIndex];
console.log(mp3Src);
return (<audio id="audio_player">
<source id="src_mp3" type="audio/mp3" src={mp3Src}/>
<source id="src_ogg" type="audio/ogg" src=""/>
<object id="audio_object" type="audio/x-mpeg" width="200px" height="45px" data={mp3Src}>
<param id="param_src" name="src" value={mp3Src} />
<param id="param_src" name="src" value={mp3Src} />
<param name="autoplay" value="false" />
<param name="autostart" value="false" />
</object>
</audio>
);
}
export default AudioPlayer;
That works perfect, however ideally I would like to either:
1 import the mp3 files straight into an array (instead of creating the array afterwards). I tried the following, however I am receiving an error of "unexpected token" at the [ bracket (when importing)
var mp3s = [];
import mp3s[0] from './sounds/0010_beat_egyptian.mp3';
import mp3s[1] from './sounds/0011_beat_euphoric.mp3';
2 Or even better import all files from the sounds folder without knowing their names into a zero indexed array.
Any help greatly appreciated. thanks.
I discovered that If you have multiple files (e.g images or mp3 files etc) that you want to include dynamically to your app then it is better to store them in the public folder (see the docs https://create-react-app.dev/docs/using-the-public-folder/).
Note: When storing your files in the public folder you don't need to "import" them but you must use the public environment variable (process.env.PUBLIC_URL) so that the correct path (to your public folder) will be referenced.
So my solution to this was:
I created a folder in the public folder called sounds (where I stored all of my mp3s for this project).
I then changed my the Audio component (in the original post) to the following:
.
import React, { Component } from 'react';
import './music-player.css';
const AudioPlayer = function(props) {
let mp3Src = process.env.PUBLIC_URL + props.sounds[props.currentSoundIndex].mp3;
return (<audio id="audio_player">
<source id="src_mp3" type="audio/mp3" src={mp3Src}/>
<source id="src_ogg" type="audio/ogg" src=""/>
<object id="audio_object" type="audio/x-mpeg" width="200px" height="45px" data={mp3Src}>
<param id="param_src" name="src" value={mp3Src} />
<param id="param_src" name="src" value={mp3Src} />
<param name="autoplay" value="false" />
<param name="autostart" value="false" />
</object>
</audio>
);
}
export default AudioPlayer;
Note: props.sounds in the above code refers to the following JavaScript array which I have in my src folder:
let sounds = [{"title" : "Egyptian Beat", "artist" : "Sarah Monks", "length": 16, "mp3" : "sounds/0010_beat_egyptian.mp3"},
{"title" : "Euphoric Beat", "artist" : "Sarah Monks", "length": 31, "mp3" : "sounds/0011_beat_euphoric.mp3"},
{"title" : "Latin Beat", "artist" : "Sarah Monks", "length": 59, "mp3" : "sounds/0014_beat_latin.mp3"},
{"title" : "Pop Beat", "artist" : "Sarah Monks", "length": 24, "mp3" : "sounds/0015_beat_pop.mp3"},
{"title" : "Falling Cute", "artist" : "Sarah Monks", "length": 3, "mp3" : "sounds/0027_falling_cute.mp3"},
{"title" : "Feather", "artist" : "Sarah Monks", "length": 6, "mp3" : "sounds/0028_feather.mp3"},
{"title" : "Lose Cute", "artist" : "Sarah Monks", "length": 3, "mp3" : "sounds/0036_lose_cute.mp3"},
{"title" : "Pium", "artist" : "Sarah Monks", "length": 3, "mp3" : "sounds/0039_pium.mp3"}];
Check this copeden here
var mp3_file = []
mp3_file[0] = 'file1',
mp3_file[1] = 'file2',
mp3_file[2] = 'file3',
mp3_file[3] = 'file4',
mp3_file[4] = 'file5',
mp3_file[5] = 'file6',
mp3_file[6] = 'file7',
mp3s = [];
for (let i=0; i<6; i++) {
mp3s[i] = mp3_file[i]
console.log(mp3s[i])
}
RESULT >
"file1"
"file2" and so on.
And also this could be interesting
var mp3_file = [];
var mp3s = [];
for (let i=0; i<6; i++) {
mp3_file[i = 'file' + i,
}
for (let i=0; i<mp3_file.length; i++) {
mp3s[i] = mp3_file[i]
console.log(mp3s[i])
}
Have "pros" and "contras" but I think you'll get the point.
If music in file are like ( songName_01.mp3 songName_02.mp3 songName_03.mp3 ...)
you can import all of them in your react project
Instead of using
var songCollection = [];
import song01 from '../some file/songName01.mp3'
import song02 from '../some file/songName02.mp3'
import song03 from '../some file/songName0.mp3'
import song04 from '../some file/songName04.mp3'
and then add them in array
Use this code transformation
var songCollection = []
function _interopRequireDefault(obj) {
return obj && obj.__esModule ? obj : { default: obj };
}
for(let i = 0; i< fileSize; i++ ) {
songCollection[i] = _interopRequireDefault(require(`../some file/songName_0${i+1}.mp3`));
}
And src value would be <audio src = {songName[2].default} ></audio>
Los-Track ( you can look at my repo how it works ) - https://github.com/sosumit001/los-track

Nested Looping inside a groovy xml builder

i am trying to create an XML file using groovy script. There is a requirement to loop two things, so that the resulting XML includes all the objects as passed by the user.
Here is the code so far, with the first loop:
import groovy.xml.*
//map to loop
def workflows = [[ name: "A", file: "fileA" , objectName: "wf_A" , objectType: "workflow", sourceRepository: "DEV2"],
[ name: 'B' , file: 'fileB' , objectName: 'wf_B' , objectType: 'workflow', sourceRepository: 'DEV2']]
// def folderNameMap = [[ srcFolder: ["srcFolder1", "srcFolder2"], TgtFolder: ["TgtFolder1", "TgtFolder2"]],
// [srcFolder: ["srcFolder3"], TgtFolder: ["TgtFolder3"]]
// ]
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
def xml = builder.bind {
mkp.xmlDeclaration()
'udm.DeploymentPackage'(version:'$BUILD_NUMBER', application: "informaticaApp"){
deployables {
workflows.each { item ->
'powercenter.PowercenterXml'(name:item.name, file:item.file){
scanPlaceholders{ mkp.yield(true) }
sourceRepository{ mkp.yield(item.sourceRepository) }
'folderNameMap' {
entry( key:"multifolder", "{{multifolderTST}}" ) // <- this is hard code and i want to remove this
}
'objectNames' {
value { mkp.yield(item.objectName) }
}
'objectTypes' {
value { mkp.yield(item.objectType) }
}
}
}
}
dependencyResolution{ mkp.yield('LATEST') }
undeployDependencies{ mkp.yield(false) }
}
}
println XmlUtil.serialize(xml)
The resultant XML is:
<?xml version="1.0" encoding="UTF-8"?><udm.DeploymentPackage version="$BUILD_NUMBER" application="informaticaApp">
<deployables>
<powercenter.PowercenterXml name="A" file="fileA">
<scanPlaceholders>true</scanPlaceholders>
<sourceRepository>DEV2</sourceRepository>
<folderNameMap>
<entry key="multifolder">{{multifolderTST}}</entry>
</folderNameMap>
<objectNames>
<value>wf_A</value>
</objectNames>
<objectTypes>
<value>workflow</value>
</objectTypes>
</powercenter.PowercenterXml>
<powercenter.PowercenterXml name="B" file="fileB">
<scanPlaceholders>true</scanPlaceholders>
<sourceRepository>DEV2</sourceRepository>
<folderNameMap>
<entry key="multifolder">{{multifolderTST}}</entry>
</folderNameMap>
<objectNames>
<value>wf_B</value>
</objectNames>
<objectTypes>
<value>workflow</value>
</objectTypes>
</powercenter.PowercenterXml>
</deployables>
<dependencyResolution>LATEST</dependencyResolution>
<undeployDependencies>false</undeployDependencies>
</udm.DeploymentPackage>
This achieves the looping for the map declared as 'workflows' . There is another entry in the XML that needs to be iterated. The section in the script is
'folderNameMap' {
entry( key:"multifolder", "{{multifolderTST}}" ) // <- this is hard code and i want to remove this
}
I need to have this section iterated and create new line entries in the resulting XML, if multiple values were supplied to the script. Like:
<folderNameMap>
<entry key="multifolder">{{multifolderTST}}</entry>
<entry key="multifolder2">{{multifolderTST2}}</entry>
<entry key="multifolder3">{{multifolderTST3}}</entry>
</folderNameMap>
How can i define this 2nd map, so that the resultant XML looks like this: (the foldermap is a map. so i will have cases where only one srcFolder and a tgtFolder was given OR there will be times when there will be multiple srcFolder anb TgtFolders were given.)
<?xml version="1.0" encoding="UTF-8"?><udm.DeploymentPackage version="$BUILD_NUMBER" application="informaticaApp">
<deployables>
<powercenter.PowercenterXml name="A" file="fileA">
<scanPlaceholders>true</scanPlaceholders>
<sourceRepository>DEV2</sourceRepository>
<folderNameMap>
<entry key="multifolder">{{multifolderTST}}</entry>
</folderNameMap>
<objectNames>
<value>wf_A</value>
</objectNames>
<objectTypes>
<value>workflow</value>
</objectTypes>
</powercenter.PowercenterXml>
<powercenter.PowercenterXml name="B" file="fileB">
<scanPlaceholders>true</scanPlaceholders>
<sourceRepository>DEV2</sourceRepository>
<folderNameMap>
<entry key="multifolder1">{{multifolderTST1}}</entry>
<entry key="multifolder2">{{multifolderTST2}}</entry>
<entry key="multifolder3">{{multifolderTST3}}</entry>
</folderNameMap>
<objectNames>
<value>wf_B</value>
</objectNames>
<objectTypes>
<value>workflow</value>
</objectTypes>
</powercenter.PowercenterXml>
</deployables>
<dependencyResolution>LATEST</dependencyResolution>
<undeployDependencies>false</undeployDependencies>
</udm.DeploymentPackage>
So, I'm taking a stab in the dark here (as I'm not 100% sure I know what your question is), but assuming your input list can be changed to:
def workflows = [
[ name: 'A',
file: 'fileA',
objectName: 'wf_A',
objectType: 'workflow',
sourceRepository: 'DEV2',
folderNames: [ multifolder: '{{multifolderTST}}',
multifolder2: '{{multifolderTST2}}' ]],
[ name: 'B',
file: 'fileB',
objectName: 'wf_B',
objectType: 'workflow',
sourceRepository: 'DEV2',
folderNames: [ multifolder3: '{{multifolderTST3}}',
multifolder4: '{{multifolderTST4}}' ]]
]
Then, you can just do:
def builder = new StreamingMarkupBuilder()
builder.encoding = 'UTF-8'
def xml = builder.bind {
mkp.xmlDeclaration()
'udm.DeploymentPackage'(version:'$BUILD_NUMBER', application: "informaticaApp"){
deployables {
workflows.each { item ->
'powercenter.PowercenterXml'(name:item.name, file:item.file) {
scanPlaceholders(true)
sourceRepository(item.sourceRepository)
folderNameMap {
item.folderNames.each { name, value ->
entry(key:name, value)
}
}
objectNames {
value(item.objectName)
}
objectTypes {
value(item.objectType)
}
}
}
}
dependencyResolution('LATEST')
undeployDependencies(false)
}
}

Is there any REST service available in Salesforce to Convert Leads into Accounts?

We have to convert Leads to accounts via REST -OAuth calls. We are able to create, update(Edit) and Detail Lead fields but not able to convert them.
We found the same is possible via SOAP API but we are following REST OAuth only.
Yes and we resolved this by creating an Apex class for REST call. Sample code is this -
#RestResource(urlMapping='/Lead/*')
global with sharing class RestLeadConvert {
#HttpGet
global static String doGet() {
String ret = 'fail';
RestRequest req = RestContext.request;
RestResponse res = RestContext.response;
String leadId = req.requestURI.substring(req.requestURI.lastIndexOf('/')+1);
Database.LeadConvert lc = new Database.LeadConvert();
lc.setLeadId(leadId);
LeadStatus convertStatus = [SELECT Id, MasterLabel FROM LeadStatus WHERE IsConverted=true LIMIT 1];
lc.setConvertedStatus(convertStatus.MasterLabel);
Database.LeadConvertResult lcr ;
try{
lcr = Database.convertLead(lc);
system.debug('*****lcr.isSuccess()'+lcr.isSuccess());
ret = 'ok';
}
catch(exception ex){
system.debug('***NOT CONVERTED**');
}
return ret;
}
}
And you can use this call by
<Your Instance URL>/services/apexrest/Lead/<LeadId>
This test will give you around 93% of coverage.
#isTest
public class RestLeadConvertTest{
static testMethod void testHttpGet() {
Lead l = new Lead();
l.FirstName = 'First';
l.LastName = 'Last';
l.Company = 'Unit Test';
insert l;
Test.startTest();
RestRequest req = new RestRequest();
RestResponse res = new RestResponse();
req.requestURI = '/Lead/' + l.Id;
req.httpMethod = 'GET';
RestContext.request = req;
RestContext.response= res;
RestLeadConvert.doGet();
Test.stopTest();
}
}
You can construct a one-off SOAP request to convert a lead and use the same OAuth token that you already have for the REST API.
The request body should look like:
<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ens="urn:sobject.partner.soap.sforce.com" xmlns:fns="urn:fault.partner.soap.sforce.com" xmlns:tns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soap:Header>
<tns:SessionHeader>
<sessionId>YOUR_OAUTH_TOKEN</sessionId>
</tns:SessionHeader>
</soap:Header>
<soap:Body>
<tns:convertLead>
<tns:leadConverts>
<tns:leadId>YOUR_LEAD_ID</tns:leadId>
<tns:convertedStatus>Closed - Converted</tns:convertedStatus>
</tns:leadConverts>
</tns:convertLead>
</soap:Body>
</soap:Envelope>
curl -H 'SOAPAction: null' -H 'Content-Type: text/xml' --data BODY_FROM_ABOVE https://your-instance-url/services/Soap/u/52.0
Note that the SOAPAction header is required, even though Salesforce does not use it.
The result will be returned as XML similar to:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns="urn:partner.soap.sforce.com" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Header>
<LimitInfoHeader>
<limitInfo>
<current>91</current>
<limit>15000</limit>
<type>API REQUESTS</type>
</limitInfo>
</LimitInfoHeader>
</soapenv:Header>
<soapenv:Body>
<convertLeadResponse>
<result>
<accountId>0015x00002C95kMAAR</accountId>
<contactId>0035x00003NjdeyAAB</contactId>
<leadId>00Q5x00001tHg1tEAC</leadId>
<opportunityId>0065x000025fDsWAAU</opportunityId>
<success>true</success>
</result>
</convertLeadResponse>
</soapenv:Body>
</soapenv:Envelope>
If you are more comfortable with JSON than XML, OneGraph provides a GraphQL API that wraps the convertLead functionality.
It's best to create your own OneGraph app to get a custom app_id, but one is provided here for demonstration purposes.
The GraphQL query will be:
mutation ConvertLead {
salesforce(
auths: {
salesforceOAuth: {
instanceUrl: "YOUR_INSTANCE_URL"
token: "YOUR_OAUTH_TOKEN"
}
}
) {
convertLead(
input: { leadConverts: [{ leadId: "YOUR_LEAD_ID" }] }
) {
leadConverts {
lead {
id
name
}
account {
name
id
}
contact {
name
id
}
opportunity {
name
id
}
success
errors {
message
statusCode
}
}
}
}
}
Then the request will look like:
curl -H 'Content-Type: application/json' 'https://serve.onegraph.com/graphql?app_id=4687c59d-8f9c-494a-ab67-896fd706cee9' --data '{"query": "QUERY_FROM_ABOVE"}'
The result will be returned as JSON that looks like:
{
"data": {
"salesforce": {
"convertLead": {
"leadConverts": [
{
"lead": {
"id": "00Q5x00001tHg1tEAC",
"name": "Daniel Boone"
},
"account": {
"name": "Company",
"id": "0015x00002C95kMAAR"
},
"contact": {
"name": "Daniel Boone",
"id": "0035x00003NjdeyAAB"
},
"opportunity": {
"name": "New Opportunity",
"id": "0065x000025fDsWAAU"
},
"relatedPersonAccountId": null,
"success": true,
"errors": []
}
]
}
}
}
}

Resources