How to Avoid Table Loading Stale Search Queries - java

I have a webapp which has performs a simple search via a REST endpoint. Each search has 0 or more arguments. How do I prevent this scenario?
User submits search request "A"
Before allowing "A" to return they modify their request and submit a new search request "B"
At this point the user expects to see the results for "B" but depending on what order the searches return either one may be displayed. How do I prevent the search results from "A" populating the table?
I am thinking about creating a hash from the search terms, sending the hash along with the search request, and comparing the hash in the return value to the hash of the most recently submitted search criteria, and only loading the request results if the hashes match.
I apologize if this question has been asked before, but I wasn't able to find it. I am using Angular 1.4 UI and a Java/Spring backend. I figure this might be a common problem with an established pattern.

You can decorate $http and add an abort method to the returned promise. This will allow you to check your promise in implementation and use the abort() to cancel the previous promise request (implementation example in the doc block below).
;(function(angular, undefined) {
angular.module('app.appConfigs')
.config(httpDecoratorConfig);
function httpDecoratorConfig($provide) {
$provide.decorator('$http', decorateHttpWithAbort);
}
/**
* decorate $http response promise with an abort function.
* use this on destroy where you want to abort the outstanding request made on
* a page before leaving the page.
*
* #example
var requestPromise = $http(reqConfig).then(...).catch(...).finally(...);
$onDestroy() {
requestPromise.abort();
}
*/
function decorateHttpWithAbort(_, $q, $delegate) {
var originalHttpService = $delegate;
function newHttpServiceConstructor(requestConfig) {
var canceller = $q.defer();
var proxyRequest = $q.defer();
var onAbortCallback = angular.noop;
// adding abortFn into request promise
proxyRequest.promise.abort = function abortFn() {
canceller.resolve();
};
// by using onAbort capture the callback function which will be called
// when the request is aborted, use this to perform cleanups.
proxyRequest.promise.onAbort = function onAbortFn(callback) {
onAbortCallback = callback;
return this;
};
// adding request canceller promise in the original request config.
requestConfig.timeout = canceller.promise;
originalHttpService(requestConfig).then(
function onSuccess() {
proxyRequest.resolve.apply(this, arguments);
},
function onError(resp) {
// don't resolve the abort response with error instead call provided
// on abort callback to give user a change to handle abort case.
// natively angular abort is resolved with error.
if (resp.status === -1) {
onAbortCallback();
return;
}
proxyRequest.reject.apply(this, arguments);
},
function onNotification() {
proxyRequest.notify.apply(this, arguments);
}
);
return proxyRequest.promise;
}
// inherit all derived methods from original $http like $get, $put etc
_.assign(newHttpServiceConstructor, originalHttpService);
return newHttpServiceConstructor;
}
})(angular);

Related

Do i have to await the resolve function when implementing hooks on sveltekit

i got this handle implementation for sveltekit hooks and because it returns a promise of response, the resolve function doesn't need to be awaited, since it is a function that either returns a value directly or returns a promise of a value, but this example from the docs awaits the function. is it ok not to await and when to and not to (if it's ok) await
export const handle:Handle=async ({event,resolve}) => {
let sid = getsid(event.request.headers.get('cookie'))
event.locals.sessionobj = getSO(sid?sid:'test')
return resolve(event)
}
It is ok to not await it.
A reason to await it would be if you want to add extra headers to the response as in the second example:
export async function handle({ event, resolve }) {
const response = await resolve(event);
response.headers.set('x-custom-header', 'potato');
return response;
}

Chrome giving Deprecation error? Fetching data from an API endpoint not working

I'm currently building a search utility using Github's API that takes in a search query, (for example, the name 'Brian'), validates the input, then displays a list of search results relevant to the query. My code/logic looks fine:
let searchBtn = document.getElementById('search-btn');
// function that takes user's input after a quick validation test
// returned value is the user's input, which is stored in the variable 'query'
let query = () => {
// validation code
let input = document.getElementById('search').value;
if(input.length < 3){
alert("Please enter more than two characters.");
}
return input;
}
searchBtn.addEventListener('click', query);
//function that retrieves data from API endpoint by concatenating API url with query variable
function getData(){
// template literal
fetch(`https://github.uconn.edu/api/v3/search/users?q=${query}`)
.then((response) => response.json())
.then(function(response){
console.log(response);
})
// catch is a callback function for when request fails
.catch(error => {
console.log(error);
})
}
searchBtn.addEventListener('click', getData);
but for some reason I am getting the following error from my Chrome console:
[Deprecation] Resource requests whose URLs contained both removed whitespace (\n, \r, \t) characters and less-than characters (<) are blocked. Please remove newlines and encode less-than characters from places like element attribute values in order to load these resources. See https://www.chromestatus.com/feature/5735596811091968 for more details.
I've tried using encodeURIComponent() as mentioned in this this link. There doesn't seem to be much info online about this particular error, or an explanation of what it means.
The link to my project: https://github.uconn.edu/pages/ssw19002/dmd-3475/Week-10/github-search/github.html
You don't need to add both functions as event listeners, because for one, the query() function isn't being called, nor the output assigned to a variable. Also, if you just define it as a function, you can call that function from the other function, which you can add as a listener.
// function that takes user's input after a quick validation test
// returned value is the user's input
function query() {
// validation code
const input = document.getElementById('search').value;
if(input.length < 3){
alert("Please enter more than two characters.");
return; // Added this so that we're not returning a value that's too short
}
return input;
}
// function that retrieves data from API endpoint by concatenating API url with query variable
function getData(){
const qParam = query();
if( !qParam ) return; // if there's no valid query value, no need to try to call the API
fetch(`https://github.uconn.edu/api/v3/search/users?q=${qParam}`)
.then((response) => response.json())
.then((response) => {
console.log(response);
})
// catch is a callback function for when request fails
.catch((error) => {
console.log(error);
})
}
searchBtn.addEventListener('click', getData);

Validate TypeError when it is undefined in ngOnInit method call

I have an input on my screen that will only display it when the filter_type is the same as the description, whenever I load the page for the first time the filter_type has no value and then the input is not shown on the screen, since I only want to show when filter_type is same as description.
There is an error occurring because #ViewChild does not fill the element on my page ... I need to handle this so no error occurs when filter_type is different from description.
Here is the code I wrote:
mycomponent.ts:
#ViewChild('filter') filter: ElementRef;
ngOnInit() {
// I tried to validate this way but there is an error anyway ...
if (typeof this.filter.nativeElement !== 'undefined') {
// The error occurs here
Observable.fromEvent(this.filter.nativeElement, 'keyup')
.debounceTime(150)
.distinctUntilChanged()
.subscribe(() => {
if (!this.dataSource) {
return;
}
this.dataSource.filter = this.filter.nativeElement.value;
});
}
}
mycomponent.html:
<mat-form-field *ngIf="filter_type === 'description'" floatPlaceholder="never" fxFlex="1 0 auto">
<input id="search" matInput #filter placeholder="Search">
</mat-form-field>
How can I hide and display the input without an error occurring updating this.dataSource.filter only when filter_type is description to filter the objects in my array?
Error that occurs at startup:
Uncaught TypeError: Cannot read property 'nativeElement' of undefined
at eval (eval at mycomponent.ngOnInit (mycomponent.component.ts:19), <anonymous>:1:13)
at ExtratoComponent.ngOnInit (mycomponent.component.ts:111)
at checkAndUpdateDirectiveInline (core.js:12369)
at checkAndUpdateNodeInline (core.js:13893)
at checkAndUpdateNode (core.js:13836)
at debugCheckAndUpdateNode (core.js:14729)
at debugCheckDirectivesFn (core.js:14670)
at Object.eval [as updateDirectives] (mycomponent_Host.ngfactory.js? [sm]:1)
at Object.debugUpdateDirectives [as updateDirectives] (core.js:14655)
at checkAndUpdateView (core.js:13802)
The #ViewChild() you are trying to access is within an *ngIf, so it does not always exist.
Use #ViewChildren() instead. It will provide you with an observable of 'current' matching element(s).
Example:
#ViewChildren('filter') filter: QueryList<any>
ngOnInit () {
this.filter.changes.subscribe(val => {
console.log(val)
})
}
Note that when using #ViewChildren(), the object type is QueryList<>. So, you have to handle it differently. Refer to the docs for #ViewChildren().
I think you might want to make use of the form control features in Angular 2. You can create a FormControl object that can manage watching for changes to the value and subscribe to that object. You wire up the template <input> to make use of the FormControl object. I put together a simple Plunker showing this behavior.
mycomponent.ts:
Initialize the form control and wire up the code for handling the changes.
searchControl = new FormControl();
ngOnInit() {
this.searchControl.valueChanges
.debounceTime(150)
.distinctUntilChanged()
.subscribe(() => {
if (!this.dataSource) {
return;
}
this.dataSource.filter = this.filter.nativeElement.value;
});
}
mycomponent.html:
Wire up the <input> to the form control.
<input ... [formControl]="searchControl">
It looks like when ngOnInit is fired, this.filter is undefined and it therefore does not have a nativeElement property.
To get rid of the error, you'd have to replace your if with:
if (typeof this.filter !== 'undefined') {
// your logic here
}
But this would mean that whenever this.filter is undefined, the Observable is not attached, so there might be cases when you have to find another event to attach it (probably making sure it's not attached twice).

Mongo result docs not store as object

I'm trying to get the results after executing a query and store it in a variable called "model".
db.collection.findOne({object},function(err,docs){
model["output"]= docs;
})
The above code stores model["output"] as "undefined". How do I get hold of this value?
Sorry there was not enough code.
So there are two files.
FILE1
dbStmtModel.insertRecords(collectionName, record).then(
function (results) {
console.log("results",results);
result = results;
}, function (err){
return err;
});
model[statement.output] = result;
FILE2
function insertRecords(operand1,operand2){
var deferred = q.defer();
db.collection(operand1).update(operand2,operand2,{upsert:true},function (err,docs) {
if(err) {
deferred.reject(err);
}
else {
deferred.resolve(docs);
}
});
return deferred.promise
}
So tried using promises, tried using async. Still do not seem to get the model store the output of the result of the query. Also, there are no errors, since the callback returns correctly and prints the results. I'm not sure I'm using promises correctly though, although the console statement seems to print the results correctly. Also I'm using mongojs and not mongoose over mongodb(since the schema here is dynamic), don't know if thats going to change anything.
First of all, check for the err param you're ignoring
There is no enough code to be sure, but I'm guessing you defined model before in the same scope you're calling findOne and just below that you try to use model['output'].
This won't work since findOne (as almost every mongodb driver method) is asynchronous, so it's unlikely for its callback to be called before you try to use model['output'].
There's no quick solution for your problem, you need to understand asynchronism. Checkout this answer.
db.collection.findOne({object},function(err,docs){
model["output"]= docs;
})
First - ill assume that your "model" object is defined. (It would throw that model is undefined, not model output)
Second, You're not checking for error, if there was error docs end up empty.
If still you've got Undefinded. There could be also error with model object. For instance - check if its even an object.
db.collection.findOne({object}, (err, docs) => {
if(err) {
return console.log(err)
}
model.output = docs;
}
Also! I'm just guessing but maybe you're trying to use it out of .findOne scope? What I mean - it is asynchronous call. So if you do something like this
db.collection.findOne({object}, (err, docs) => {
if(err) {
return console.log(err)
}
model.output = docs;
}
console.log(model.output);
then your model.output is undefined cause you call it before database returns data - it does not wait. You'll have to use callback (or promise) then.
callDB (object, cb) => {
db.collection.findOne(object, (err, docs) => {
if(err) {
return (err)
}
return (null, docs);
}
}
then you could call it
callDB({object}, (err, result) => {
model.result = result;
});
But be advised that your new call for function is still asynchronous. So still your model.result will work only inside of its scope.
// I've seen you've updated your question, but I'll leave it here.
First be sure that you are getting your results make a
console.log("RESULT",docs) if you getting your results then try below methods
As mongo query return doc which is a model schema that can not be modify.
Try this with lean it return document JSON object
var model={};
db.collection.findOne({object},function(err,docs){
model.output= docs;
}).lean(true);
OR
var result={}
db.collection.findOne({object},function(err,docs){
result.model.output = docs;
})

Execute multiple tasks asynchronously and return first successful result in JavaScript function

I have to write a javaScript function that return some data to the caller.
In that function I have multiple ways to retrieve data i.e.,
Lookup from cache
Retrieve from HTML5 LocalStorage
Retrieve from REST Backend (bonus: put the fresh data back into cache)
Each option may take its own time to finish and it may succeed or fail.
What I want to do is, to execute all those three options asynchronously/parallely and return the result whoever return first.
I understand that parallel execution is not possible in JavaScript since it is single threaded, but I want to at least execute them asynchronously and cancel the other tasks if one of them return successfully result.
I have one more question.
Early return and continue executing the remaining task in a JavaScript function.
Example pseudo code:
function getOrder(id) {
var order;
// early return if the order is found in cache.
if (order = cache.get(id)) return order;
// continue to get the order from the backend REST API.
order = cache.put(backend.get(id));
return order;
}
Please advice how to implement those requirements in JavaScript.
Solutions discovered so far:
Fastest Result
JavaScript ES6 solution
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Promise.race(iterable)
Returns a promise that resolves when the first promise in the iterable resolves.
var p1 = new Promise(function(resolve, reject) { setTimeout(resolve, 500, "one"); });
var p2 = new Promise(function(resolve, reject) { setTimeout(resolve, 100, "two"); });
Promise.race([p1, p2]).then(function(value) {
// value == "two"
});
Java/Groovy solution
Ref: http://gpars.org/1.1.0/guide/guide/single.html
import groovyx.gpars.dataflow.Promise
import groovyx.gpars.dataflow.Select
import groovyx.gpars.group.DefaultPGroup
import java.util.concurrent.atomic.AtomicBoolean
/**
* Demonstrates the use of dataflow tasks and selects to pick the fastest result of concurrently run calculations.
* It shows a waz to cancel the slower tasks once a result is known
*/
final group = new DefaultPGroup()
final done = new AtomicBoolean()
group.with {
Promise p1 = task {
sleep(1000)
if (done.get()) return
10 * 10 + 1
}
Promise p2 = task {
sleep(1000)
if (done.get()) return
5 * 20 + 2
}
Promise p3 = task {
sleep(1000)
if (done.get()) return
1 * 100 + 3
}
final alt = new Select(group, p1, p2, p3, Select.createTimeout(500))
def result = alt.select()
done.set(true)
println "Result: " + result
}
Early Return and Interactive Function
Angular Promises combined with ES6 generators???
angular.module('org.common')
.service('SpaceService', function ($q, $timeout, Restangular, $angularCacheFactory) {
var _spacesCache = $angularCacheFactory('spacesCache', {
maxAge: 120000, // items expire after two min
deleteOnExpire: 'aggressive',
onExpire: function (key, value) {
Restangular.one('organizations', key).getList('spaces').then(function (data) {
_spacesCache.put(key, data);
});
}
});
/**
* #class SpaceService
*/
return {
getAllSpaces: function (orgId) {
var deferred = $q.defer();
var spaces;
if (spaces = _spacesCache.get(orgId)) {
deferred.resolve(spaces);
} else {
Restangular.one('organizations', orgId).getList('spaces').then(function (data) {
_spacesCache.put(orgId, data);
deferred.resolve(data);
} , function errorCallback(err) {
deferred.reject(err);
});
}
return deferred.promise;
},
getAllSpaces1: function (orgId) {
var deferred = $q.defer();
var spaces;
var timerID = $timeout(
Restangular.one('organizations', orgId).getList('spaces').then(function (data) {
_spacesCache.put(orgId, data);
deferred.resolve(data);
}), function errorCallback(err) {
deferred.reject(err);
}, 0);
deferred.notify('Trying the cache now...'); //progress notification
if (spaces = _spacesCache.get(orgId)) {
$timeout.cancel(timerID);
deferred.resolve(spaces);
}
return deferred.promise;
},
getAllSpaces2: function (orgId) {
// set up a dummy canceler
var canceler = $q.defer();
var deferred = $q.defer();
var spaces;
$timeout(
Restangular.one('organizations', orgId).withHttpConfig({timeout: canceler.promise}).getList('spaces').then(function (data) {
_spacesCache.put(orgId, data);
deferred.resolve(data);
}), function errorCallback(err) {
deferred.reject(err);
}, 0);
if (spaces = _spacesCache.get(orgId)) {
canceler.resolve();
deferred.resolve(spaces);
}
return deferred.promise;
},
addSpace: function (orgId, space) {
_spacesCache.remove(orgId);
// do something with the data
return '';
},
editSpace: function (space) {
_spacesCache.remove(space.organization.id);
// do something with the data
return '';
},
deleteSpace: function (space) {
console.table(space);
_spacesCache.remove(space.organization.id);
return space.remove();
}
};
});
Personally, I would try the three asynchronous retrievals sequentially, starting with the least expensive and ending with the most expensive. However, responding to the first of three parallel retrievals is an interesting problem.
You should be able to exploit the characteristic of $q.all(promises), by which :
as soon as any of the promises fails then the returned promise is rejected
if all promises are successful then the returned promise is resolved.
But you want to invert the logic such that :
as soon as any of the promises is successful then the returned promise is resolved
if all promises fail then the returned promise is rejected.
This should be achievable with an invert() utility which converts success to failure and vice versa.
function invert(promise) {
return promise.then(function(x) {
return $q.defer().reject(x).promise;
}, function(x) {
return $q.defer().resolve(x).promise;
});
}
And a first() utility, to give the desired behaviour :
function first(arr) {
return invert($q.all(arr.map(invert)));
}
Notes:
the input arr is an array of promises
a native implementation of array.map() is assumed (otherwise you can explicitly loop to achieve the same effect)
the outer invert() in first() restores the correct sense of the promise it returns
I'm not particularly experienced in angular, so I may have made syntactic errors - however I think the logic is correct.
Then getOrder() will be something like this :
function getOrder(id) {
return first([
cache.get(id),
localStorage.get(id).then(cache.put),
backend.get(id).then(cache.put).then(localStorage.put)
]);
}
Thus, getOrder(id) should return a Promise of an order (not the order directly).
The problem in your example getOrder lies in that if the 3 lookup functions are going to be asynchronous, you won't get the order back from them right away and as they are not blocking, the getOrder would return null; You would be better off defining a callback function which takes action on the first returned order data and simply ignores the rest of them.
var doSomethingWithTheOrder = function CallBackOnce (yourResult) {
if (!CallBackOnce.returned) {
CallBackOnce.returned = true;
// Handle the returned data
console.log('handle', yourResult);
} else {
// Ignore the rest
console.log('you are too late');
}
}
Make your data lookup functions accept a callback
function cacheLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from cache');
}, 3000);
}
function localeStorageLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from locale storage');
}, 1500);
}
function restLookUp(id, callback) {
// Make a real lookup here
setTimeout(function () {
callback('order data from rest');
}, 5000);
}
And pass the callback function to each of them
function getOrder(id) {
cacheLookUp(id, doSomethingWithTheOrder);
localeStorageLookUp(id, doSomethingWithTheOrder);
restLookUp(id, doSomethingWithTheOrder);
}
Create a broadcast event in your api calls, then create $scope.$on to listen to those broadcast, when $on get's activated do the function that refreshes those objects.
So in your service have a function that makes an ajax calls to your rest api.
You would have 3 ajax calls. And 3 listeners. Each of them would look something like this.
This is just sudo code, but this format is how you do it
something like
$http({
method: "GET",
url: url_of_api,
}).success(function(data, *args, *kwargs){
$rooteScope.$braodcast('success', data)
})
In your controller have a listener something like this
$scope.$on('success', function(event, args){
// Check the state of the other objects, have they been refreshed - you probably want to set flags to check
if (No Flags are Set):
$scope.data = args // which would be the returned data adn $scope.data would be what you're trying to refresh.
}

Categories

Resources