I am using IBM BPM 8.6
I have an input string as follows:
"\"RECORD_CONTACT\":\"Maram\" , \"DRUG\":\"Panadol\"
In a script on server side, I want to dynamically create a business object like this:
tw.local.recordContact = Maram;
tw.local.drug = Panadol;
How can I dynamically create the business object?
There are a few problems with your request. The first is that you are not creating a business object, you are creating variables. In IBM BPM the variables have to be declared at design time or you will get an error, so invoking attempting to call something like -
tw.local.myVariable = 'Bob';
Will throw an exception if tw.local.myVariable has not been declared. Base on your other question you asked here (link), I'm going to assume you actually have an ANY variable declared called "return" so that
tw.local.return.myVariable = 'Bob'
will work. Given that I based on Sven's answer I think something like the following will work (you will need to validate)
var str = "\"RECORD_CONTACT\":\"Maram\" , \"DRUG\":\"Panadol\"";
var jsonStr = "{" + str.replace(/\\\"/g,'\"') + "}";
var tempValue = JSON.parse(jsonStr);
var keyArray = Object.keys(tempValue);
var valueArray = Object.values(tempValue);
for(var keyCount=0; keyCount<keyArray.length; keyCount++{
var evalString = "tw.local.return."+keyArray[keyCount]+"="+valueArray[keyCount];
eval(evalString);
}
I'll note that doing this is a very bad idea as it would be very brittle code and that using eval() in this manner opens you up to all sorts of possible exploits. It will also fail badly if the value for one of the keys is not a simple type.
-Andrew Paier
One should know what you are going to do with dynamically created Business Objects (BO) to answer you better. Like a very generic way would be - creating JSON object instead of BO.
But if you want to stick with BO then this is only possible when you know all the BO structure (schema) beforehand during design time.
var str = "\"RECORD_CONTACT\":\"Maram\" , \"DRUG\":\"Panadol\"";
vat objArray = str.split("reg ex to split each object string")
foreach (obj in objArray ){
if(obj.indexOf( "RECORD_CONTACT")!=-1)
tw.local.recordContact = new tw.object.RECORD_CONTACT();
//below goes code get value of each attribute of BPM from string
}
else if(obj.indexOf( "DRUG")!=-1){
//similar code to create BO DRUG
}
Don't forget to create BO before using those :)
I'm trying to debug some code that another programmer has left for me to maintain. I've just attempted to upgrade from node.js 5 to node.js 8 and my database queries are for some requests coming back with key not found errors
We're using couchbase for the database and our document keys are "encrypted" for security. So we may have a key that starts like this "User_myemail#gmail.com" but we encrypt it using the following method:
function _GetScrambledKey(dbKey)
{
//select encryption key based on db key content
var eKeyIndex = CalculateEncryptionKeyIndex(dbKey, eKeys.length);
var sha = CalculateSHA512(dbKey + eKeyIndex);
return sha;
}
function CalculateEncryptionKeyIndex(str, max)
{
var hashBuf = CalculateSHA1(str);
var count = 0;
for (var i = 0; i < hashBuf.length; i++)
{
count += hashBuf[i];
count = count % max;
}
return count;
}
We then query couchbase for the document with
cb.get("ECB_"+encryptedKey, opts, callback);
In node5 this worked but in node8 we're getting some documents return fine and others return as missing. I outputted the "ECB_"+encryptedKey as an int array and the results have only confused me more. They are different on node5 to node8 but only by one character right in the middle of the array.
Outputting the encryptedKey as an int array on both versions shows this
188,106,14,227,211,70,94,97,63,130,78,246,155,65,6,148,62,215,47,230,211,109,35,99,21,60,178,74,195,13,233,253,187,142,213,213,104,58,168,60,225,148,25,101,155,91,122,77,2,99,102,235,26,71,157,99,6,47,162,152,58,181,21,175
Then outputting the concatenated string, in the same way, shows slightly different results
This is the node8 output
Node8 key: 69,67,66,95,65533,106,14,65533,65533,70,94,97,63,65533,78,65533,65533,65,6,65533,62,65533,47,65533,65533,109,35,99,21,60,65533,74,65533,13,65533,65533,65533,65533,65533,65533,104,58,65533,60,65533,25,101,65533,91,122,77,2,99,102,65533,26,71,65533,99,6,47,65533,65533,58,65533,21,65533
And this is the node5 output
Node5 key: 69,67,66,95,65533,106,14,65533,65533,70,94,97,63,65533,78,65533,65533,65,6,65533,62,65533,47,65533,65533,109,35,99,21,60,65533,74,65533,13,65533,65533,65533,65533,65533,65533,104,58,65533,60,65533,65533,25,101,65533,91,122,77,2,99,102,65533,26,71,65533,99,6,47,65533,65533,58,65533,21,65533
I had to run it through a diff tool to see the difference
Comparing that to the original pre-append array it looks like the 225 has just been dropped in node8. Is 225 significant? I can't understand how that would be possible otherwise unless it's a bug. Does anyone have any ideas?
Looks like this was a change in v8 5.5 https://github.com/nodejs/node/issues/21278
A lot of the issues you are facing, including the concatenation can be cleaned up using newer features from ES6 that are available in node 8.
In general, you should avoid doing string concatenations with the + operator and should use string literals instead. In your case, you should replace the "ECB_"+encryptedKey with `ECB_${encryptedKey}`.
Additionally, if you want to output the contents of the integers values from this concatenated string, then you are better off using .join, the spread operator (...) and the Buffer class from Node as follows:
let encKey = `ECB_${encryptedKey}`;
let tmpBuff = Buffer.from(encKey);
let buffArrVals = [...tmpBuff];
console.log(buffArrVals.join(','));
Also, if you can help it, you really should avoid using var inside of function blocks like it exists in your sample code. var performs something called variable hoisting and causes the variable to become available outside the scope it was declared, which is seldom intended. From node 6+ onward the recommendation is to use let or const for variable declarations to ensure they stay scoped to the block they are declared.
I have been able to run the Github project Word-Add-in-JS-Redact successfully. Now, I need to make a change in the code. Need help for that.
At present the code finds multiple occurrences of a single string in a Word Doc and highlights those. But, I need to search multiple strings (can be hard coded inside the js file as well) in the document with a single button click, and highlight those. For example, I want to find both strings 'this' and 'that' in the document at the same time, and highlight them.
The current code, which searches for a single string:
Word.run(function (context) {
//search string
var searchInput = "hello";
// Search the document.
var searchResults = context.document.body.search(searchInput, {matchWildCards: true});
// Load the search results and get the font property values.
context.load(searchResults, "");
// Synchronize the document state by executing the queued-up commands,
// and return a promise to indicate task completion.
return context.sync()
.then(function () {
var count = searchResults.items.length;
// // Queue a set of commands to change the font for each found item.
for (var i = 0; i < count; i++) {
searchResults.items[i].font.highlightColor = '#FFFF00'; //Yellow
}
return count;
})
.then(context.sync)
.then(reportWordsFound);
A couple of ways I have tried so far with no luck:
Ran context.document.body.search(searchInput, in a loop of the search strings, and tried to append the searchResults strings using + and push. This attempt gave an error saying, I cannot add multiple context results to a single object.
I tried to be creative with WildCards operators, but nothing was suitable for this. Many posts are talking about JS regex \(string1|string2/), but this seems to be invalid in Word context.
I could solve the problem at last, by creating a parent function, which calls the search function in a loop. My mistake was to create the loop inside the search function itself.
Here is the working code:
function searchStrings(){
searchString('hello world');
searchString('Goodbye');
}
RedactAddin.searchStrings = searchStrings;
function searchString(input) {
// Run a batch operation against the Word object model.
Word.run(function (context) {
//...... (same as the original) ...............
Update: A Solution below!
I'm fairly new to website development but I've been tasked with developing e2e (end-to-end) tests for a developing website that uses AngularJS. As a result I've been looking down the road of using AngularJS's karma-run ngScenario testing wrapper.
Anyway, just getting started I want to make sure that a simple hyperlink's text matches part of its href address. There isn't need to know the structure of this code snippet, but these are thumbnails for user profiles, and you can click the thumbnail object completely (the first 'a') or you can click a link displaying their name (the second 'a').
There dozens of these on a page.
Here is what a part of the page looks like loaded with a user "PurplePenguin".
<div class="thumbnail__section">
<a href="/profile/PurplePenguin">
<div class="thumbnail__bottom">
<div class="thumbnail__rating ng-binding"> </div>
<div class="thumbnail__info">
<div class="thumbnail__name">
<a class="ng-binding" href="/profile/PurplePenguin">PurplePenguin</a>
</div>
[...]
Essentially I want a test that will take the text of the second 'a' element and check it against the href attribute: "assert that href '/profile/PurplePenguin' equals '/profile/' + 'PurplePenguin'"
This is what I've made just wanting to test the first thumbnail's 'a', (in my time writing "PurplePengiun" is the first user every time so I could hard code it).
it('should have the performer\'s name match the link', function() {
// "eq(n)" gets the nth instance of the element;
// "> a:eq(0)" grabs its first child a
var nameElement = element('.thumbnail__name:eq(0) > a:eq(0)');
// These work and pass
expect(nameElement.text()).toBe('PurplePenguin');
expect(nameElement.attr('href')).toBe('/profile/PurplePenguin');
// This does not
var textString = nameElement.text(); // textString == "[object Object]"
expect(nameElement.attr('href')).toBe('/profile/' + textString);
This gets returned:
expect element '.thumbnail__name:eq(0) > a:eq(0)' get attr 'href' toEqual "/profile/[object Object]"
expected "/profile/[object Object]" but was "/profile/PurplePenguin"
So I've figured out how to find the particular element on the page I need, but trying to manipulate the text of a simple 'a' element only gives me [object Object].
Some things I've tried with my basic knowledge of JS:
nameElement.text().toString(), nameElement.text().value(), nameElement[0].text()
Trying things other than text()
nameElement.html() and nameElement.val()
They too return [object Object] when trying to use them as strings.
It seems that looking at the values and attributes of these elements only works when using the very specific API functions like .toBe or .toEqual, but I want to assert that a specifically formatted string is made.
Thank you for any help!
Solution
Thanks for that bit of insight Andyrooger, I had actually taken a stab at the query function before posting my question but gave up on it too quick. Your explanation gave me the idea to start looking deeper into the samples that have been posted in the official docs. I ended up taking a hint from a Adi Roiban's post to another Angular e2e writer's question talking about query, done() messages, and promises. This ended up leading me to my eventual solution.
So I've made myself a solution and in the spirit of cooperation made a set of examples for others to learn by. There are four examples, the first two are just getting the text and the href and comparing them against hard-coded values. The third one uses indexOf to do dirt-simple comparisons. The fourth one shows how you can make your own more specific pass/fail conditions (more than what Jasmine provides with its matchers).
Number 1: User name text vs hard coded value
it('should have the user\'s name be \'PurplePenguin\'', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text(); // Can finally access this guy!
// The first param null indicates a nominal execution, the second param is a return of sorts
done(null, text);
});
// Passes
expect(textPromise).toBe('PurplePenguin')
});
Number 2: Profile href value vs hard coded value
it('should have the user\'s link be \'/profile/PurplePenguin\'', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var href = nameElement.attr('href');
// The first param null indicates a nominal execution, the second param is a return of sorts
done(null, href);
});
// Passes
expect(textPromise).toBe('/profile/PurplePenguin')
});
Number 3: Simple string comparison
it('should have the user\'s name match the link', function() {
var textPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text();
var href = nameElement.attr('href');
// The first param null indicates a clean pass, the second param is a return of sorts
done(null, href.indexOf(text)); // indexOf returns -1 if text is _not_ a sub-string of href
});
expect(textPromise).toBeGreaterThan(-1);
// In the case of failure reports this in the console (if using Karma):
// expect element .thumbnail__name:eq(0) > a:eq(0) custom query toBeGreaterThan -1
// expected -1 but was -1
// In the runner.html page for the test simply reports this useless/misleading log:
// expected -1 but was -1
});
Number 4: More detailed string comparison doing it your own way plus a better error message
it('should have the user\'s name match the link', function() {
var nameStringPromise = element('.thumbnail__name:eq(0) > a:eq(0)').query(function (nameElement, done) {
var text = nameElement.text();
var href = nameElement.attr('href');
var message;
if (href.indexOf(text) == -1)
message = 'Did not find "' + text + '" in "' + href + '"';
done(message, href.indexOf(text));
});
expect(nameStringPromise);
// An expect() with no Jasmine matcher means that it only uses the done message to determine
// a pass or fail for the test. So I wrote that if my conditions weren't met it would print
// an informative message in the case of failure
// Example failure output with a broken url generator:
// Did not find "PurplePenguin" in "/profile/"
});
I'm struggling to find references to back this up or explain a little more nicely, other than these docs which don't have much info. Therefore this answer is mainly from memory of reading the relevant code.
When you write a test in ngScenario, you are not writing a series of immediate actions as you would do in a unit test. What you are actually doing queuing up a list of commands to execute when the test starts.
Methods that looks like normal jQuery functions, such as text() or attr() are actually part of ngScenario's DSL and return future objects (meaning they will at some point execute and have the result of the call you wanted.)
expect() already knows this so waits for the result before comparing to your expected value. The code underneath, in your example, is reading directly and immediately so will see the future object.
If you do need to read the element in this way I suggest looking at the query function which should allow it. On the other hand if you need to just check the formatting of the string you can use toMatch which has been implemented in ngScenario too.