Apex salesforce pagereference for URL link mergefield - report

Assuming a link is created and is pointing to a URL with the following pattern:
/00O70000001SOsa?pv0={!Account.Id}&pv1={!Case.IsClosed}
How can this be written in an Apex controller if I have to invoke the same thing using pagereference or any other similar API.

// You will have to apply your own logic on how you pick data for the link
Account acc = [SELECT Id FROM Account LIMIT 1];
Case c = [SELECT IsClosed FROM Case LIMIT 1];
Pagereference pr = new PageReference('/00O70000001SOsa');
pr.getParameters().putAll(new Map<String, String>{
'pv0' => acc.Id,
'pv1' => c.isClosed ? 'true' : 'false'
});
System.debug(pr.getUrl());
// outputs something like /00O70000001SOsa?pv0=0017000001TSmKmAAL&pv1=false
Parameters you pass to the URL have to be Strings (or something that easily casts to String). So it's your job to convert booleans, display dates in format that the other page will "like" (reports need dates in the same format the user would use so according to his/her locale, look at format method in Date and DateTime class).
But that's it. You don't have to worry about "?" sign or any fancy escaping of special characters, it'll be done for you:
Pagereference pr = new PageReference('/home/home.jsp');
pr.getParameters().putAll(new Map<String, String>{
'spaces' => 'spa ce',
'html-entities' => '& < >',
'percents' => '1 / 4 = 25%'
});
System.debug(pr.getUrl());
// /home/home.jsp?html-entities=%26+%3C+%3E&percents=1+%2F+4+%3D+25%25&spaces=spa+ce

Related

How can I generate a url to a new AppInsights query?

I have a process that generates AppInsights telemetry. I would like to prove a link to a query in AppInsights. However, it is not the same query every time - the parameters change. I know I can share a link to an existing query, but how do I generate such a link to a new query?
In your Application Insights Query Editor, we have an option called Copy link to query. In this link we have following details:
The URL generated from this action has the following format:
https://portal.azure.com/## TENANT_ID/blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%2Fsubscriptions%2F SUBSCRIPTION_ID %2FresourceGroups%2F< RESOURCEGROUP%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2F APPLICATION INSIGHTS_INSTANCE_NAME /source/LogsBlade.AnalyticsShareLinkToQuery/q/ ENCODED
BASE 64_KQL_QUERY /timespan/TIMESPAN
I’ve emphasized in bold here the parameters of the URL. These parameters have the following values:
TENANT_ID: Your Tenant ID
SUBSCRIPTION_ID: Your Azure Subscription ID that contains the Application Insights instance.
RESOURCE_GROUP: Your Resource Group where the Application Insights instance is deployed.
APPINSIGHTS_INSTANCE_NAME: Your Application Insights instance Name.
ENCODED_KQL_QUERY: Base64 encoding of your query text zipped and URL encoded
TIMESPAN: time filter for the query (optional).
If your query has less than 1600 characters, you can also replace the q parameter in the above URL with a query parameter, and the encoded string will simply be your query plain text escaped (without zipping and encoding).
Dynamic URL it’s important to:
Take the text of your KQL query
Zip it
Encode it in Base64
A C# code that does the encoding of the KQL query is the following:
Generate the Query whatever you want and pass that into the below function to get the Encoded base 64 URL and you can add this in a base URL of application insights.
static string Encodedbase64KQLQuery(string query)
{
var bytes = System.Text.Encoding.UTF8.GetBytes(query);
using (MemoryStream memoryStream = new MemoryStream())
{
using (GZipStream compressedStream = new GZipStream(memoryStream, CompressionMode.Compress, leaveOpen: true))
{
compressedStream.Write(bytes, 0, bytes.Length);
}
memoryStream.Seek(0, SeekOrigin.Begin);
Byte[] bytedata = memoryStream.ToArray();
string encodedBase64Query = Convert.ToBase64String(bytedata);
return HttpUtility.UrlEncode(encodedBase64Query);
}
}
Please visit this blog which helped me a lot.
Thanks Delliganesh and Stefano from the blog link. Here is a simple JavaScript example. Be sure to replace all 4 constant values at top and the sessionId when calling the function. You can also tweak the query, but just keep in mind the 1600 character limit as described above and in the blog.
const APP_INSIGHTS_INSTANCE_NAME = "APP_INSIGHTS_INSTANCE_NAME";
const APP_INSIGHTS_RESOURCE_GROUP = "APP_INSIGHTS_RESOURCE_GROUP";
const APP_INSIGHTS_SUBSCRIPTION_ID = "APP_INSIGHTS_SUBSCRIPTION_ID";
const APP_INSIGHTS_TENANT_ID = "APP_INSIGHTS_TENANT_ID";
const getAppInsightsQueryUrl = ({ sessionId }) => {
const query = `requests | where session_Id == "${sessionId}"`;
const url = `https://portal.azure.com/##${APP_INSIGHTS_TENANT_ID}/blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/resourceId/%2Fsubscriptions%2F${APP_INSIGHTS_SUBSCRIPTION_ID}%2FresourceGroups%2F${APP_INSIGHTS_RESOURCE_GROUP}%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2F${APP_INSIGHTS_INSTANCE_NAME}/source/LogsBlade.AnalyticsShareLinkToQuery/query/${encodeURI(
query
)}/timespan/TIMESPAN`;
return url;
};
getAppInsightsQueryUrl({
sessionId: 'my-session-id',
})

How to access field name with space in Cloud Function? [duplicate]

This question already has answers here:
How can I access a JavaScript object which has spaces in the object's key?
(4 answers)
Closed 1 year ago.
The example code is below:
exports.updateUser = functions.firestore
.document('users/{userId}')
.onUpdate((change, context) => {
// Get an object representing the document
// e.g. {'First Name': 'Marie', 'age': 66}
const newValue = change.after.data();
// ...or the previous value before this update
const previousValue = change.before.data();
// access a particular field as you would any JS property
//const name = newValue.name; //How does one accesses 'FIRST NAME'
// perform desired operations ...
});
If 'FIRST NAME' is replaced by 'name' it is easy, but my data comes from somewhere else where I have no control on the field names. So the field descriptions have spaces between. How do I read them please?
You need to use brackets notation of property accessor for that:
newValue["First Name"]
In the object[property_name] syntax, the property_name is just a string or Symbol. So, it can be any string, including '1foo', '!bar!', or even ' ' (a space).

How to parse a collection's sub-object to find a unique result from many possibilities?

In my user's schema, I have a TokAuth Array with token sub-objects (like multiple mails addresses).
So in a method, when I search the tokens in the current user :
var id = Meteor.userId();
var usercurrent = Meteor.users.findOne({_id: id}, {fields: {"TokAuth": 1}});
var userToken = usercurrent.TokAuth.token;
I got in console.log(userToken)
[ 'fyAyXkXYrQdAlNpjuQfJ8RLU2TpfVGLnptlBs-m1h7xk',
I20170224-20:36:23.202(1)? 'YTwtUbhNTgiEfzFbJq7mESnOoOHeLYxWlqEeJJIG_GiV',
I20170224-20:36:23.206(1)? 'ViA4ydDITJtHDi2c_sArkNtpRYTjFqGL1ju2v00_-rFJ',
I20170224-20:36:23.206(1)? '51ImZcxRADLJr-FPCUL7EFGnTZYjHSZk3XxdqtBV2_fd',
I20170224-20:36:23.207(1)? 'S5aEvqjJ5zTUJqLFCPY1aZ1ZhsQppZTJtYKULM9aS2B3',
I20170224-20:36:23.207(1)? 'mhBs3oxHf2SxZfu2vCZhtiyPfg25fKMY8bKMZD8fx6IG',
I20170224-20:36:23.207(1)? '-rv0FiP-lxoqe8INyCJASV6rZpbgy3euEqB9sO9HsZSV',
I20170224-20:36:23.207(1)? 'zacr6_VBjHTsArov1LmQyZFLwI40fx4J7sygpLosTrli' ]
Beside, I've got a var who is equal to the last token in the userToken sub-object (that's of course expected : not to be the last one, but to be in the sub-object).
console.log (editAuth);
zacr6_VBjHTsArov1LmQyZFLwI40fx4J7sygpLosTrli
So how can I parse userToken to find a match with editAuth? If userToken was just a String, it will be simple but here...
Thanks
Is there a reason you are storing all the tokens as an array as opposed to just updating a single string each time?
That aside, you can check if an array contains a value by using the handy underscore function _.contains
Example:
_.contains( userToken, editAuth ); //returns true or false
In this case, you are simply trying to search for a string within an array of strings. #Sean already provided one solution.
If you are using the meteor ecmascript package then you can just simply use the native Array.includes method.
userToken.includes(editAuth);
On a side note, after using ECMAScript 2015+ for some time now, I find that I can use the native API for almost everything that I used to use underscore or lodash for. Check it out!

Different data types in form and database and forward and backward conversion

I thought it'd be easy but, yeah... it wasn't. I already posted a question that went in the same direction, but formulated another question.
What I want to do
I have the collection songs, that has a time attribute (the playing-time of the song). This attribute should be handled different in the form-validation and the backend-validation!
! I'd like to do it with what autoform (and simple-schema / collection2) offers me. If that's possible...
in the form the time should be entered and validated as a string that fits the regex /^\d{1,2}:?[0-5][0-9]$/ (so either format "mm:ss" or mmss).
in the database it should be stored as a Number
What I tried to do
1. The "formToDoc-way"
This is my javascript
// schema for collection
var schema = {
time: {
label: "Time (MM:SS)",
type: Number // !!!
},
// ...
};
SongsSchema = new SimpleSchema(schema);
Songs.attachSchema(SongsSchema);
// schema for form validation
schema.time.type = String // changing from Number to String!
schema.time.regEx = /^\d{1,2}:?[0-5][0-9]$/;
SongsSchemaForm = new SimpleSchema(schema);
And this is my template:
{{>quickForm
id="..."
type="insert"
collection="Songs"
schema="SongsSchemaForm"
}}
My desired workflow would be:
time is validated as a String using the schema
time is being converted to seconds (Number)
time is validated as a Number in the backend
song is stored
And the way back.
I first tried to use the hook formToDoc and converted the string into seconds (Number).
The Problem:
I found out, that the form validation via the given schema (for the form) takes place AFTER the conversion in `formToDoc, so it is a Number already and validation as a String fails.
That is why I looked for another hook that fires after the form is validated. That's why I tried...
2. The "before.insert-way"
I used the hook before.insert and the way to the database worked!
AutoForm.hooks({
formCreateSong: {
before: {
insert: function (doc) {
// converting the doc.time to Number (seconds)
// ...
return doc;
}
},
docToForm: function (doc) {
// convert the doc.time (Number) back to a string (MM:SS)
// ...
return doc;
}
}
});
The Problem:
When I implemented an update-form, the docToForm was not called so in the update-form was the numerical value (in seconds).
Questions:
How can I do the way back from the database to the form, so the conversion from seconds to a string MM:SS?
Is there a better way how to cope with this usecase (different data types in the form-validation and backend-validation)?
I am looking for a "meteor autoform" way of solving this.
Thank you alot for reading and hopefully a good answer ;-)
I feel like the time should really be formatted inside the view and not inside the model. So here's the Schema for time I'd use:
...
function convertTimeToSeconds (timeString) {
var timeSplit = timeString.split(':')
return (parseInt(timeSplit[0]) * 60 + parseInt(timeSplit[1]))
}
time: {
type: Number,
autoValue: function () {
if(!/^\d{1,2}:?[0-5][0-9]$/.test(this.value)) return false
return convertTimeToSeconds(this.value)
}
}
...
This has a small disadvantage of course. You can't use the quickForm-helper anymore, but will have to use autoForm.
To then display the value I'd simply find the songs and then write a helper:
Template.registerHelper('formateTime', function (seconds) {
var secondsMod = seconds % 60
return [(seconds - secondsMod) / 60, secondsMod].join(':')
})
In your template:
{{ formatTime time }}
The easy answer is don't validate the string, validate the number that the string is converted into.
With simpleschema, all you do is create a custom validation. That custom validation is going to grab the string, turn it into a number, and then validate that number.
Then, when you pull it from the database, you'll have to take that number & convert it into a string. Now, simpleschema doesn't do this natively, but it's easy enough to do in your form.
Now, if you wanted to get fancy, here's what I'd recommend:
Add new schema fields:
SimpleSchema.extendOptions({
userValue: Match.Optional(Function),
dbValue: Match.Optional(Function),
});
Then, add a function to your time field (stored as Date field):
userValue: function () {
return moment(this.value).format('mm:ss');
},
dbValue: function () {
return timeToNumber(this.value);
}
Then, make a function that converts a timeString to a number (quick and dirty example, you'll have to add error checking):
function timeToNumber(str) {
str.replace(':',''); //remove colon
var mins = +str.substr(0,2);
var secs = +str.substr(2,2);
return mins * 60 + secs;
}
Then, for real-time validation you can use schema.namedContext().validateOne. To update the db, just send timeToNumber(input.value).

How to pass a collection name as an argument into methods in Meteor?

Thanks my poor English skill, I have o express my idea by these code below..
Friendly edit:
I am trying to write a generalized confirmAndRemoveCollection method which takes in the collectionName and itemId, and I would like to perform operations on this collection. Since collectionName is a string, I wouldn't be able to perform DB operations on it. Could someone please suggest how I could use the collection name to get access to the actual collection object.
confirmAndRemoveCollection:(collectionName,itemId)->
check(itemId,String)
check(collectionName,String)
sweetAlert({
title:"confirm"
text:"blabla"
type:"info"
showCancelButton: true,
confirmButtonColor: "#DD6B55",
confirmButtonText: "delete"
cancelButtonText: "cancel"
closeOnConfirm: false,
},(isConfirm)->
if isConfirm
collectionName.remove(itemId)
else
return
swal(
'success'
"selected item deleted"
"success"
)
The variable collectionName is a string object, so you won't be able to call MongoDB methods on it.
One way to accomplish your task is to create an object that maps the string name to the collection object.
For example:
Posts = new Mongo.Collection('posts');
Comments = new Mongo.Collection('comments');
Collections = {
'Posts': Posts,
'Comments': Comments
};
Then you could do something like this in your code
if isConfirm
Collections[collectionName].remove(itemId)
Just to add an alternative here (even though the question is really old): you can pass the collection itself as an argument and it will work.
As the collection is an Object, when you pass it as an argument it will be passed "by reference" and you will be able to call its methods.
Following the example by #FullStack (which also works, of course):
Posts = new Mongo.Collection('posts');
Comments = new Mongo.Collection('comments');
const collectionRemove = (collection, id) => {
const count = collection.remove(id);
console.log(`Removed ${count} items with id ${id} from collection ${collection._name}`)
}
And then do something like:
collectionRemove(Posts, 1);
collectionRemove(Comments, 24);

Resources