Search with the URL Query Parameters on Flow Router - javascript

I use Search Source, and Flow Router from Arunoda. They both work great, but I'm just struggling to get them work together.
I have a helper that returns some documents rendered from the server-run SearchSource method like this:
Template.search.helpers({
things: function() {
var currentUserId = Meteor.userId();
var langParam = FlowRouter.current().queryParams.lang;
console.log(langParam);
return BookSearch.getData({
sort: {date_added: -1}
});
}
});
As you see, I'm just trying to search for things that are registered in the language entered at the URL (e.g. 'en') as a query parameter. Let's say in "english" as in this example:
http://localhost:3000/search?lang=en
I can perfectly read the "en" and log on the console by the below code, but it does not work to search with. I mean because of this code:
var langParam = FlowRouter.current().queryParams.lang;
console.log(langParam);
I get "en" printed on the browser console. But I don't get the things that are registered in "en" language.
So how can I achieve a proper search using the query parameters?
What I need to know is how to enter in the helper to render only data that fetches to the condition I want (in this case, english language - {lang: langParam}. For that one uses the Package.getData() API, but I could not locate exactly how.

First of all, searchsource sets up necessary data delivery for you so you don't have to, indeed should not set up publications or subscriptions for your search flow. There's tons of literature around for how pub/sub works in Meteor so I'll skip ahead to your searchsource problem.
I see that you want to scope your search to a certain language. Here's a basic set up that would get you going. You should also fine tune things like throttling, metadata handling, limiting, paging, input and query param sanitization, result transformations etc.
Template
<template name="booksearch">
<form name="booksearch"><input type="search"/></form>
<ul>
{{#each hits}}
<li>{{title}}</li>
{{#each}}
</ul>
</template>
Client: set up your helper
var options = {
// cache the search results for 5 minutes
keepHistory: 1000 * 60 * 5,
// allow fast local searches on the cache
localSearch: true
};
// feed the search to the title field only
var fields = ['title'];
// Set up your search
BookSearch = new SearchSource('books', fields, options);
/*
get the search results reactively. mind you, this is not an invocation.
you'll invoke the search within your event handler down below
*/
Template.booksearch.helpers({
hits : function() {
return BookSearch.getData();
}
})
Template.booksearch.events({
'submit form': function(e,t) {
// listen for the submit event
e.preventDefault();
var options = {
// this is your lang query param from the url
lang: FlowRouter.getQueryParam('lang')
};
// value of the search input from your template
var searchText = t.$('input').val();
// invoke the search using the input and the language
BookSearch.search(searchText,options);
}
})
Server: set up your search
SearchSource.defineSource('books', function(searchText, options) {
// make sure you do have a lang option or use a default one
var lang = options.lang || 'english'
if(searchText) {
var regExp = buildRegExp(searchText);
// use the input and lang to build your mongodb selector
var selector = {title: regExp, language: lang};
return Books.find(selector).fetch();
} else {
// don't return anything if nothing is searched for
return [];
}
});
function buildRegExp(searchText) {
// copied over from the naive github example
var parts = searchText.trim().split(/[ \-\:]+/);
return new RegExp("(" + parts.join('|') + ")", "ig");
}

Related

addPreSearch filter not applying

I am trying to use the addPreSearch function to add a custom filter to a lookup field, but the function does not seem to execute fully before the results of the lookup are displayed. The code for this looks something like this:
function onFieldChange(executionContext) {
var formContext = executionContext.getFormContext();
formContext.getControl("test_code").removePreSearch(testFunctionFilter);
formContext.getControl("test_code").addPreSearch(testFunctionFilter);
}
function testFunctionFilter(executionContext) {
var formContext = executionContext.getFormContext();
var record1 = formContext.getAttribute("test_record1_link").getValue(); //get linked record
var record1FullId, record1Id, stringRecordId, idLength, record1Guid = "0";
if (record1 != null) {
record1Id = record1[0].id;
record1Id = record1FullId.slice(1, -1);
stringRecordId = record1FullId.toString();
idLength = stringRecordId.length;
//Guid when retrieved from tablet does not have parenthesis on each end
if (idLength == 36) {
record1Guid = record1FullId;
} else {
record1Guid = recordId;
}
}
var fieldValue;
Xrm.WebApi.retrieveRecord("test_record1", record1Guid, "?$select=test_field1")
.then(function(result1) {
fieldValue = result1.test_field;
var options = generateOptions(executionContext, fieldValue); //creates option string using retrieved fieldValue
Xrm.WebApi.retrieveMultipleRecords("test_record2", options)
.then(function(result) {
var codes = getCodes(result2, fieldValue);
filter = generateFilter(codes, record1Guid); //creates custom filter using provided parameters
console.log(filter); //displays filter correctly
formContext.getControl("test_codelookup").addCustomFilter(filter, "test_coderecord"); //not working?
});
});
}
The filter is generated correctly using the functions used above whose definitions aren't shown. That isn't the issue. I've tried creating a separate test function where I hard coded one of the filters that the function above generated, and the lookup displayed the correct results. The testFunctionFilter should run to completion before the results of the lookup are displayed, correct? Because the filter is logged to the console after the results of the lookup appear. Are the nested asynchronous Xrm.WebApi calls somehow causing the issue? I'm not quite sure what is wrong. Please advise.
You are right. Xrm.WebApi calls are always Asynchronous, which is unusable in this case of adding dynamic filter using addCustomFilter.
You have to use XMLHttpRequest and make that call as Synchronous by setting third parameter as false like below:
var req = new XMLHttpRequest();
req.open("GET", Xrm.Utility.getGlobalContext().getClientUrl() +
"/api/data/v9.0/test_record1?$select=test_field1", false);
In order to work around the async delay, I think you're going to have to reorganise your code:
Add a form OnLoad event and execute the query to retrieve test_field1 and cache the results in a parameter
In the OnChange event, remove the presearch filter, re-execute the query to retrieve test_field1 and update the same parameter (from onload)
In testFunctionFilter use the cached results rather than building the presearch filter from scratch

Understanding JavaScript for TFS widget

I've been trying to modify the sample dashboard widget at this location
https://learn.microsoft.com/en-us/vsts/extend/develop/add-dashboard-widget?view=vsts#part-2-hello-world-with-vsts-rest-api
However, reluctantly have to admit I simply can't understand the structure required to extend it
Near the end, it uses "load: function" and returns the outputs of a REST API call, which I can consume however I want
However, I need to make more than one different REST call, and I simply cannot figure out how to get that info usable in my function
I modified the code so it starts like this:
VSS.require(["TFS/Dashboards/WidgetHelpers", "TFS/Work/RestClient","VSS/Service", "TFS/WorkItemTracking/RestClient" ],
I then created a handle for the other call I want to make like this:
var queryClient = VSS_Service.getCollectionClient(TFS_Wit_QueryAPI.WorkItemTrackingHttpClient);
var queryResults = queryClient.getQuery(projectId, "Shared Queries/My Bugs");
However, I cannot consume the contents of queryResults - I know it's working up to a point as if I put in an invalid URL it will error as it knows it can't access anything there. If the URL is correct, no matter what I've tried - even stringify just to see what comes back - I get 'undefined' or something similar (it's definitely a valid JavaScript object)
The key seems to be right at the end when you have "load: function" except that only allows one thing to be returned? The reason I know this is if I change the function that it returns to be the one I've written rather than the one from the sample, it works fine - but the problem remains the same in that I can only process the results of one API call.
You can call more than one APIs, the code in that article is just the simple sample.
For Widget extension, you just need to return the status (e.g. Success()) in load function, so you can return status at the end of the function. For example:
var getQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to VSTS
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Feedback")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName: "<unknown>") ));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return true;
}, function (error) {
// Use the widget helper and return failure as Widget Status
console.log(error);
return false;
});
}
var getAnOhterQueryInfo = function (widgetSettings) {
// Get a WIT client to make REST calls to VSTS
return TFS_Wit_WebApi.getClient().getQuery(projectId, "Shared Queries/Bug")
.then(function (query) {
// Create a list with query details
var $list = $('<ul>');
$list.append($('<li>').text("Query ID: " + query.id));
$list.append($('<li>').text("Query Name: " + query.name));
$list.append($('<li>').text("Created By: " + (query.createdBy ? query.createdBy.displayName: "<unknown>") ));
// Append the list to the query-info-container
var $container = $('#query-info-container');
$container.empty();
$container.append($list);
// Use the widget helper and return success as Widget Status
return true;
}, function (error) {
// Use the widget helper and return failure as Widget Status
console.log(error);
return false;
});
}
return {
load: function (widgetSettings) {
// Set your title
var $title = $('h2.title');
$title.text('Hello World');
var r1= getQueryInfo(widgetSettings);
var r2=getAnOhterQueryInfo(widgetSettings);
if(r1==true && r2==true){
return WidgetHelpers.WidgetStatusHelper.Success();
}else{
return WidgetHelpers.WidgetStatusHelper.Failure("failed, check error in console");
}
}

Access Firebase database

I have a project with Firebase. I want to have access to the attribute "text" of my database with a cloud function as soon as there is a recent branch added to my database.
I am not get used to using their database.
My code in NodeJs is bellow :
exports.messageAnswer = functions.database.ref('/messages')
.onWrite(event => {
const snapshot = event.data;
const val = snapshot.val();
var textMsg = val.text;
var regex = "(bot)";
//var database = firebase.database();
if(!textMsg.match(regex) && textMsg.length > 0){
var id = new Date().getTime().toString();
var object = {
text : "I am the Cloud bot #" + id,
};
admin.database().ref('/messages').push(object);
}
return 0;
});
My problem is the fact that there is a "PUSH ID" : I don't know how to contourne this unique string to get the value of the attribute "text".
You can use wildcards to get the text attribute some something like this:
exports.messageAnswer = functions.database.ref('/messages/{pushid}')
.onWrite(event => {
for more info check this: https://firebase.google.com/docs/database/extend-with-functions
You can specify a path component as a wildcard by surrounding it with curly brackets; ref('foo/{bar}') matches any child of /foo. The values of these wildcard path components are available within the event.params object of your function. In this example, the value is available as event.params.bar
Your current code is being triggered whenever any data under /messages changes. From your description it seems you only want to be triggered when a new message is added. You do that by using onCreate instead of onWrite. In addition you'll want to listen for changes to a specific message, instead of to the entire /messages node:
exports.messageAnswer = functions.database.ref('/messages/{messageId}')
.onCreate(event => {
Also see Doug's blog post on these triggers: https://firebase.googleblog.com/2017/07/cloud-functions-realtime-database.html

Accessing original field in Parse.com Cloud Code beforeSave function

The ultimate goal is to detect changes between an existing Parse object and the incoming update using the beforeSave function in Cloud Code.
From the Cloud Code log available through parse.com, one can see the input to beforeSave contains a field called original and another one called update.
Cloud Code log:
Input: {"original": { ... }, "update":{...}
I wonder if, and how, we can access the original field in order to detect changing fields before saving.
Note that I've already tried several approaches for solving this without success:
using (object).changedAttributes()
using (object).previousAttributes()
fetching the existing object, before updating it with the new data
Note on request.object.changedAttributes():
returns false when using in beforeSave and afterSave -- see below for more details:
Log for before_save -- summarised for readability:
Input: { original: {units: '10'}, update: {units: '11'} }
Result: Update changed to { units: '11' }
[timestamp] false <--- console.log(request.object.changedAttributes())
Log for corresponding after_save:
[timestamp] false <--- console.log(request.object.changedAttributes())
There is a problem with changedAttributes(). It seems to answer false all the time -- or at least in beforeSave, where it would reasonably be needed. (See here, as well as other similar posts)
Here's a general purpose work-around to do what changedAttributes ought to do.
// use underscore for _.map() since its great to have underscore anyway
// or use JS map if you prefer...
var _ = require('underscore');
function changesOn(object, klass) {
var query = new Parse.Query(klass);
return query.get(object.id).then(function(savedObject) {
return _.map(object.dirtyKeys(), function(key) {
return { oldValue: savedObject.get(key), newValue: object.get(key) }
});
});
}
// my mre beforeSave looks like this
Parse.Cloud.beforeSave("Dummy", function(request, response) {
var object = request.object;
var changedAttributes = object.changedAttributes();
console.log("changed attributes = " + JSON.stringify(changedAttributes)); // null indeed!
changesOn(object, "Dummy").then(function(changes) {
console.log("DIY changed attributes = " + JSON.stringify(changes));
response.success();
}, function(error) {
response.error(error);
});
});
When I change someAttribute (a number column on a Dummy instance) from 32 to 1222 via client code or data browser, the log shows this:
I2015-06-30T20:22:39.886Z]changed attributes = false
I2015-06-30T20:22:39.988Z]DIY changed attributes =
[{"oldValue":32,"newValue":1222}]

LDAP JS with Node JS Finding users passing in patterns of string

I am currently using LDAP JS for Authentication in Angular JS app and everything works perfectly fine.
I am now building a new view and the requirement I have is this:
I have text box in which admin will write may be a few letters of a user id present in LDAP.
I want to show app matching ID present in LDAP on a typeahead/suggestions. I know how typeahead works so that's not an issue. The issue is how can I pass a rejex or pattern matching kind of a thing for uid in search() method.
My sample code is here:
function GetAllLDAPUser(dnFilter, res) {
client.search('uid=**WHAT-PATTERN-CAN-I-PASS-HERE**' + dnFilter, opts, function(err, result) {
result.on('searchEntry', function(entry) {
// I usually read entry.object or entry.raw here , that works
});
result.on('end', function(result) {
.......
});
}
}
}
So the question is what should I pass in place of
WHAT-PATTERN-CAN-I-PASS-HERE
Results :
Suppose I type an. The typeahead will show all user id starting with an like ana, anamon, analisa etc.
I have written the final solution and closed the issue on the project's repository
For pattern matching, we need to play with the 'filter' field in option object which we pass to the search method. So I ended up doing something like below:
var dnFilter = 'ou=People,o=Intra,dc=YOURCOMPANY,dc=com'; //depends on your LDAP settings.
var query;
var matchedUsers = [];
query.LDAPName = "dummy"; //some name which resides in LDAP
//You can even have one simple variable rather than having this query object.
opts = {
scope: 'sub',
filter: (shcDisplayName = '+ query.LDAPName + ')
'
};
//Do not use 'shcDisplayName' , this will be any variable stored in your LDAP object. You need get
//the structure of LDAP end point you are working on. For me, I had one variable 'shcDisplayName'
//on which I wanted to play so I am using this variable in my filter.
client.search(dnFilter, opts, function(err, result) {
result.on('searchEntry', function(entry) {
matchedUsers.push({
'Name': entry.object.shcDisplayName,
'Id': entry.object.uid
});
}
result.on('end', function(result) {
if (matchedUsers.length) { //if any match was found.
//send the json result back
res.json(matchedUsers);
//if you want to send json back, do not use res.send() otherwise you will end up getting
//circular reference error.
}
}
result.on('error', function(ex) {
//Handle errors here if any
});
});
}
}

Categories