Authorization interceptor with Infinity-scroll in AngularJS - javascript

I'm using angular-http-auth for intercepting 401 response in order to display login dialogue and when the user is authorized, to retry failed request.
Since I'm using infinity-scroll I'm increasing an offset value, with every additional upload:
var upload = function () {
dataResource.query($scope.model).then(function (result) {
angular.forEach(result.items, function (value) {
$scope.items.push(value);
});
});
}
$scope.uploadMore = function () {
$scope.model.Offset = $scope.model.Offset + 10;
upload();
};
upload();
When my page loads up it immediately sends 2 request to server upload(), invoked from this directive, and uploadMore() by infinity-scroll.
However, after user has logged in, the page does not display the first 10 entries, instead it displays 11-20 items 2 times in a row.
When I tried to debug it, I noticed that when angular-http-auth retries requests it uses increased by 10 Offset value for both queries($scope.module argument).
Functions upload() and uploadMore() are running for 2 times before angular-http-auth, so I guess that is why interceptor uses updated argument for both queries.
Could somebody please help me with this problem?

So you can resolve this problem prevent execute request until previous will finish.
The faster way to do that is :
var pending = false;
var upload = function () {
if(!pending) {
pending = true;
dataResource.query($scope.model).then(function (result) {
pending = false;
angular.forEach(result.items, function (value) {
$scope.items.push(value);
});
});
}
}

Related

postman.setNextRequest failing to call

I have a collection of tests which I am trying to set up to run in a certain sequence. The sequence is decided using if/else if statements which check a variable in a data-file and set the next request depending on its value.
This works well for the initial request in my collection. But when it gets to a request later in the sequence, the if statement will recognize the correct variable but skip over the postman.setNextRequest() command and run the test below it in the collection order instead of running the specified test.
Does anyone know why this is happening? Seems like a bug with the postman.setNextRequest() function.
Request where function is working as expected:
var rsp = pm.response.json();
var base = pm.environment.get('baseUrl');
pm.environment.set('id', data['appName']);
var id = pm.environment.get('id');
pm.test("Status code is 200", function () {
pm.response.to.have.status(200);
pm.test("Response contains correct BaseUrl", function(){
var issuer = rsp.issuer;
pm.expect(issuer).to.contain(base);
//console.log(resName);
console.log('Passed - on to the next one!')
});
});
if (id === 'Application/Member_PRODUCTION'||id === 'Application/B2C_PRODUCTION'||id === 'Application/Claims Transformation_PRODUCTION'){
postman.setNextRequest('PKCE Step 1- Generate PKCE Challenge and Verifier');
}
else if (id === 'Application/sysTest_SysTest2_PRODUCTION'){
postman.setNextRequest("oAuth Step 1 - Unauthorised API call");
}
else{
postman.setNextRequest("oAuth Step 2- Get Session Data Key");
}
Next request where the function fails to work:
let cookieJar = pm.cookies.jar();
cookieJar.clear(pm.environment.get('baseUrl'));
var id = pm.environment.get('id');
console.log('oAuth check complete for application: '+ id);
pm.test("Status code is 302", function () {
pm.response.to.have.status(302);
console.log('Passed - on to the next one!')
});
if (id === 'Application/Member_PRODUCTION'){
console.log('Next Up - SCIM Tests')
postman.setNextRequest("SCIM Step 1 - Create Member");
}
else{
console.log('Next Up - Login Page Test')
postman.setNextRequest("Login Page Step 1- Get Session data key");
}
The request names were copied straight from the requests themselves so I don't believe that it cannot find the specified next request and the usage in both snippets above seems to be the same so I am a little stuck.

HTTP requests until header has attachment

We have a web application which upon visiting the url, will prepare and generate a .zip file which is then downloaded.
I need to create a nodejs application using requestjs that can keep making requests until there is an attachment header, from which point it would be downloaded.
The page which generates the .zip file contains a simple html message, stating that the file is being prepared for download. With a javascript reload(true) function called on load.
I'm not sure if this is the right way of doing it, but I am open to suggestions.
You could use async.until to loop through some logic until the header is available:
let success = true;
async.until(
// Do this as a test for each iteration
function() {
return success == true;
},
// Function to loop through
function(callback) {
request(..., function(err, response, body) {
// Header test
if(resonse.headers['Content-Disposition'] == 'attatchment;filename=...') {
response.pipe(fs.createWriteStream('./filename.zip'));
success = true;
}
// If you want to set a timeout delay
// setTimeout(function() { callback(null) }, 3000);
callback(null);
});
},
// Success!
function(err) {
// Do anything after it's done
}
)
You could do it with some other ways like a setInterval, but I would choose to use async for friendly asynchronous functionality.
EDIT: Here's another example using setTimeout (I didn't like the initial delay with setInterval.
let request = require('request');
let check_loop = () => {
request('http://url-here.tld', (err, response, body) => {
// Edit line below to look for specific header and value
if(response.headers['{{HEADER_NAME_HERE}}'] == '{{EXPECTED_HEADER_VAL}}')
{
response.pipe(fs.createWriteStream('./filename.zip')); // write file to ./filename.zip
}
else
{
// Not ready yet, try again in 30s
setTimeout(check_loop, 30 * 1000);
}
});
};
check_loop();

Windows 8 Javascript app activation/launch deferral

So currently in a windows 8 WinJS app I'm coding, I am trying to get the loading of an xml file to take place in the app startup sequence, while the splash screen is still showing, as this xmldoc element is needed for when the home page loads, and loading of the home page will fail without it.
This is my initiation sequence in default.js:
(function () {
"use strict";
var activation = Windows.ApplicationModel.Activation;
var app = WinJS.Application;
var nav = WinJS.Navigation;
var sched = WinJS.Utilities.Scheduler;
var ui = WinJS.UI;
app.addEventListener("activated", function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
console.log("Newly Launched!");
var localSettings = Windows.Storage.ApplicationData.current.localSettings;
WinJS.Namespace.define("MyGlobals", { localSettings: localSettings });
// APP RUN TYPE CHECK AND SEQUENCE (FIRST RUN / NOT FIRST RUN):
if (MyGlobals.localSettings.values['firstRunCompleted']) {
console.log("NOT FIRST RUN!");
// CACHE VERSION CHECK. IF APP HAS BEEN UPDATED, INITIATE NEWLY ADDED CACHE VALUES HERE:
} else {
console.log("FIRST RUN!")
MyGlobals.localSettings.values['firstRunCompleted'] = true;
};
//loadXML(); have tried many things with this. doesn't work.
} else {
// TODO: This application has been reactivated from suspension.
// Restore application state here.
var currentVolume = app.sessionState.currentVolume;
if (currentVolume) {
console.log("RESTORE FROM SUSPENSION");
console.log(currentVolume);
};
}
nav.history = app.sessionState.history || {};
nav.history.current.initialPlaceholder = true;
// Optimize the load of the application and while the splash screen is shown, execute high priority scheduled work.
ui.disableAnimations();
var p = ui.processAll().then(function () {
return nav.navigate(nav.location || Application.navigator.home, nav.state);
}).then(function () {
return sched.requestDrain(sched.Priority.aboveNormal + 1);
}).then(function () {
ui.enableAnimations();
});
args.setPromise(p);
args.setPromise(WinJS.UI.processAll().then(function completed() {
loadSavedColour();
// Populate Settings pane and tie commands to Settings flyouts.
WinJS.Application.onsettings = function (e) {
e.detail.applicationcommands = {
"helpDiv": { href: "html/Help.html", title: WinJS.Resources.getString("settings_help").value },
"aboutDiv": { href: "html/About.html", title: WinJS.Resources.getString("settings_about").value },
"settingsDiv": { href: "html/Settings.html", title: WinJS.Resources.getString("settings_settings").value },
};
WinJS.UI.SettingsFlyout.populateSettings(e);
}
As you can see where I have the commented line of "loadXML()", that is where I need the loadXML() function to take place.
Here is my loadXML() function:
function loadXML() {
Windows.ApplicationModel.Package.current.installedLocation.getFolderAsync("foldername").then(function (externalDtdFolder) {
externalDtdFolder.getFileAsync(MyGlobals.localSettings.values['currentBook']).done(function (file) {
Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file).then(function (doc) {
WinJS.Namespace.define("MyGlobals", {
xmlDoc: doc,
});
})
})
});
};
(loadXML is a working function and works in other scenarios)
However, the issue is that before the loadXML function finishes, the app splash screen goes away, and the next home.html home page loads, which starts the accompanying home.js, which has a function that requires the MyGlobals.xmlDoc object that loadXML should have made. This immediately crashes the app, as MyGlobals.xmlDoc is undefined/null.
I used to have this app working by running loadXML in home.js for the home.html page directly, but in that scenario the XML document is reloaded every time navigation is made to the page, wasting time and resources. As such, I'm trying to move the xmldocument loading into the app startup/initialization.
Thanks a lot!
loadXML has async functionality and you need to handle that.
You shouldn't expect that the loadFromFileAsync (or any of the other async functions) have completed before it returns to the caller. If your code doesn't wait, you'll find that the MyGlobals.xmlDoc value won't be set when you need it.
I've renamed it below to be more accurate as to its behavior. The big change is that it returns a Promise that can be used by the caller to properly wait for the Xml doc to be loaded. This Promise could be used with other Promises to wait on multiple conditions if you'd like (or in the case, other async work).
function loadXMLAsync() {
return new WinJS.Promise(function (complete, error, progress) {
var localSettings = MyGlobals.localSettings.values;
var installedLocation = Windows.ApplicationModel.Package.current.installedLocation;
installedLocation.getFolderAsync("foldername").then(function (externalDtdFolder) {
externalDtdFolder.getFileAsync(values['currentBook']).done(function (file) {
Windows.Data.Xml.Dom.XmlDocument.loadFromFileAsync(file).then(function (doc) {
complete(doc);
});
});
});
});
};
Then, in use:
loadXmlAsync().then(function(doc) {
WinJS.Namespace.define("MyGlobals", {
xmlDoc: doc,
});
// and any other code that should wait until this has completed
});
The code above does not handle errors.
I believe what you need to do is to extend the splash screen to give your app more time to initialize UI. For your scenario, it's loading the xml.
I will suggest you read How to extend the splash screen (HTML). The main idea is to display an extended splash screen(you can use the same image as the default one) in the activated event, then call your loadXML.
In addition to other comments, what you really need to do is include the promise from loadXml with what you pass to args.setPromise. As you know, setPromise is a way to tell the app loader to wait until that promise is fulfilled before removing the splash screen. However, in your code you're calling setPromise multiple times. What you should be doing is joining all the promises you care about (animations, loadXml, and setting loading) with WinJS.Promise.join, so that you get a single promise that's then waiting on all the other three, and when that one is fulfilled, then remove the splash screen.
Alan's suggestion for an extended splash screen is helpful if that whole loading process ends up taking too long, as doing an extended splash gives you total control over what's happening and when the transition happens to your main page.

How to kill another function

I have a function that does request to API when the video is finished:
video.addEventListener('ended', example);
var example = function () {
VK.api('video.get', { owner_id: 123 }, function(data) {
/**...*/
}
}
And also I have a replay button (shows when video is finished), which the user can click faster than the response comes from API. And now I need to kill my function. How I can do it?
Link to API: https://vk.com/dev/video.get
There don't seems to be any method to cancel a all from this api.
So either you analyse the code to see how is handled that call (Can't do it for you because we need an auth).
I'd say that your best bet is to set a boolean flag to true while making the call and to false when asking for replay :
//Our flag
var userCanceledCalled = false;
var example = function () {
document.querySelector('span').innerHTML = "new example should be called in few seconds";
//Set it to fals at call
userCanceledCalled = false;
VK.api('video.get', { owner_id: 123 }, function(data) {
if(!userCanceledCalled){
alert(data);
}
});
}
function replay(){
//Set it to true on user action
document.querySelector('span').innerHTML = "new example as been canceled";
userCanceledCalled = true;
}
var b = document.querySelectorAll('button');
b[0].addEventListener('click', example);
b[1].addEventListener('click', replay);
var VK={api: function(data, useless, fn){setTimeout(function(){fn(data)}, 3000)}};
<button>Call a new Example</button></br>
<button>Replay</button>
<span id="log"></span>
There are two approaches:
Cancel the ongoing request
Prevent the callback function from doing work
I have no idea how to do 1. but you can probably ask at the Vkontakte issue tracker https://vk.com/bugs
As for the second one, you can check if the user has already clicked on replay button by the time the response comes back and turn your callback into a noop:
video.addEventListener('ended', function () {
VK.api('video.get', { owner_id: 123 }, function (data) {
// Let's check if user has pressed the replay button and
// if yes then we abort the function:
if (userHasPressedReplay) { return; }
/**...*/
};
});
This is not ideal because the request still hits the server... but it prevents execution of the callback function.

chrome.hid.send fails on second use

Something about my use of chrome.hid.send seems to be leaving the bus in a bad state. I consistently can NOT get my second usage of the API call to work. Sometimes, it will also fail on the first usage. WITH THE EXACT SAME CODE, I can come back and try a while later (maybe 10min) and the first send will work.
The device I'm working with does not return a response to all messages sent to it. The test message for example, is just a dummy message that is ignored by the device. I've tested this both on a mac and a PC. My call stack depth is 2 at this point in my application (literally first one is kicked off by a button click and then a setTimeout calls the same method 5s later).
I've testing sending buffers of length 64Bytes as well as 58Bytes. The properties from the HidDeviceInfo object read "maxInputReportSize":64,"maxOutputReportSize":64
Params on first usage:
Params on second usage:
I really can't identify how I'm using the API incorrectly. When messages do succeed, I can see them on the device side.
// Transmits the given data
//
// #param[in] outData, The data to send as an ArrayBuffer
// #param[in] onTxCompleted, The method called on completion of the outgoing transfer. The return
// code is passed as a string.
// #param[in] onRxCompleted, The method called on completion of the incoming transfer. The return
// code is passed as a string along with the response as an ArrayBuffer.
send: function(outData, onTxCompleted, onRxCompleted) {
if (-1 === connection_) {
console.log("Attempted to send data with no device connected.");
return;
}
if (0 == outData.byteLength) {
console.log("Attempted to send nothing.");
return;
}
if (COMMS.receiving) {
console.log("Waiting for a response to a previous message. Aborting.");
return;
}
if (COMMS.transmitting) {
console.log("Waiting for a previous message to finish sending. Aborting.");
return;
}
COMMS.transmitting = true;
var dummyUint8Array = new Uint8Array(outData);
chrome.hid.send(connection_, REPORT_ID, outData, function() {
COMMS.transmitting = false;
if (onTxCompleted) {
onTxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '');
}
if (chrome.runtime.lastError) {
console.log('Error in COMMS.send: ' + chrome.runtime.lastError.message);
return;
}
// Register a response handler if one is expected
if (onRxCompleted) {
COMMS.receiving = true;
chrome.hid.receive(connection_, function(reportId, inData) {
COMMS.receiving = false;
onRxCompleted(chrome.runtime.lastError ? chrome.runtime.lastError.message : '', inData);
});
}
});
}
// Example usage
var testMessage = new Uint8Array(58);
var testTransmission = function() {
message[0] = 123;
COMMS.send(message.buffer, null, null);
setTimeout(testTransmission, 5000);
};
testTranmission();
The issue is that Windows requires buffers to be the full report size expected by the device. I have filed a bug against Chromium to track adding a workaround or at least a better error message to pinpoint the problem.
In general you can get more detailed error messages from the chrome.hid API by enabling verbose logging with the --enable-logging --v=1 command line options. Full documentation of Chrome logging is here.

Categories