TFS Rest API: Extension Data Service unreliable? - tfs

My TFS hub extension (on-premise 2015.3) does not load correctly because of unexpected extension data service behaviour and not getting its needed preferences. The extension users store - after installation once during first-start/loading the hub page - extension preferences on the collection-level, as key-value pairs (getValue/setValue from extension data service API), and if the hub page gets reloaded the preferences are stored already. It's like an Application Wizard/First start Dialog in my hub page.
But when I install the extension on another collection of the same TFS and want to store (=setValue) preferences for that collection it comes back with OK (can see it in F12->network capture of Internet Explorer), but cannot find these previously entered/stored key-value pairs when refreshing (=getValue on the key) my hub. It delivers an empty value for the key, and the "first-start" dialog reappears again, what shouldnt happen if there was a value for the key. Already debugged, it always comes back empty (empty value) in that collection. No error from the service, nothing to capture, nothing to debug.
Can I check somewhere else (on the TFS logs, event viewer, or database) for deeper debugging?
I also tried with Powershell and restcalls manually by putting and getting the json on the Rest urls for the extension data service. In one collection it works (manually and per hub extension) but for the other collection the data service does not work.
Is there a known issue in 2015.3 in the extension data service? I really have a problem if I cannot store the preferences of the extension anywhere - storing it to an default source control path would be an alternative, but I do not want to force the projects to check-in preferences for my extension...
EDIT:
Adding relevant code snippet
function showSourceControlDialog(project: string/*TFS_Core_Contracts.TeamProjectReference*/) {
return Q.Promise(function (resolve, reject) {
//setTimeout(function () {
var thatProjectIDclean = project/*.id*/.replace(/-/g, "_");
var enterSourceControlPathDialog = VSS_Controls_Dialogs.show(VSS_Controls_Dialogs.ModalDialog, {
title: "Please now enter source control path for " + thisProjectName /*project.name*/,
content: $("<p/>").addClass("confirmation-text").html("<label><b>Source Control Path</b> to preferences file, e.g. '$/" + thisProjectName /*thisCollectionName + "/" + project.name*/ + "/.../...xml:</label><input id='enterSourceControlPathInput' size='40'/>" /*+ projectName + ".xml"*/),
useBowtieStyle: true,
buttons: {
"Cancel": function () {
enterSourceControlPathDialog.close();
enterSourceControlPathDialog.dispose();
reject("cancelled");
},
"OK": function () {
sourceControlPath = $("input#enterSourceControlPathInput").val();
if (sourceControlPath) {
setConfiguration(thatProjectIDclean, sourceControlPath).then(function (setToThisPath) {
console.log(setToThisPath);
enterSourceControlPathDialog.close();
enterSourceControlPathDialog.dispose();
$(".bss-button").show();
$(".bss-tvc").show();
resolve(sourceControlPath);
}).catch(function (error) {
reject(error);
})
}
}
}
});
//}, 10000);
});
}
function setConfiguration(key: string, value: string) {
return Q.Promise(function (resolve, reject) {
// Get data service
VSS.getService(VSS.ServiceIds.ExtensionData).then(function (dataService: IExtensionDataService) {
// Set value in collection scope
dataService.setValue(pssVersion + "_" + key, value/*, { scopeType: "Project Collection" }*/).then(function (setToThis: string) {
console.log(pssVersion + "_" + key + " is now " + setToThis );
resolve(setToThis);
}, function (error) {
reject(error);
console.log(error);
}, function (error) {
reject(error);
console.log(error);
});
});
}
function getConfiguration(key: string) {
return Q.Promise(function (resolve, reject) {
// Get data service
VSS.getService(VSS.ServiceIds.ExtensionData).then(function (dataService: IExtensionDataService) {
// Get value in collection scope
dataService.getValue(pssVersion + "_" + key/*, { scopeType: "Project Collection" }*/).then(function (gotThis: string) {
sourceControlPath = gotThis;
console.log(pssVersion + "_" + key + " is " + gotThis );
resolve(gotThis);
}, function (error) {
reject(error);
console.log(error);
});
}, function (error) {
reject(error);
console.log(error);
});
});
}
try {
console.log(thisProjectIDclean);
getConfiguration(thisProjectIDclean).then(function (resultPath: string) {
console.log(resultPath);
console.log(sourceControlPath);
if (!resultPath) {
//getProjects().then(function (resultProjects: TFS_Core_Contracts.TeamProjectReference[]) {
// resultProjects.forEach(function (resultProject: TFS_Core_Contracts.TeamProjectReference) {
showSourceControlDialog(thisProjectID/*resultProject*/).then(function () {
getXMLTree();
}, function (error) {
console.log(error);
});
// }, function (error) {
// console.log(error);
// });
//}, function (error) {
// console.log(error);
//});
} else {
getXMLTree();
}
});
} catch (error) {
console.log(error);
}

Unfortunately what you are trying to do is not possible. Referring to the example the "maximum" scope you can store extension data in is the "Project Collection".
From my experience while fiddling with ExtensionDataService it is not possible to query data from a "foreign" collection.

Related

how to combine multiple instances of navigator.serviceWorker.register('sw.js')

for example in my index.html I have a code to detect an update for service worker and the code is like this:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js').then(reg => {
reg.addEventListener('updatefound', () => {
// A wild service worker has appeared in reg.installing!
newWorker = reg.installing;
newWorker.addEventListener('statechange', () => {
// Has network.state changed?
switch (newWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// new update available
showUpdateBar();
}
// No update available
break;
}
});
});
});
let refreshing;
navigator.serviceWorker.addEventListener('controllerchange', function () {
if (refreshing) return;
window.location.reload();
refreshing = true;
});
}
then in pushManager.js the code is like:
navigator.serviceWorker.register('sw.js')
.then(function (registration) {
messaging.useServiceWorker(registration);
// Request for permission
messaging.requestPermission()
.then(function() {
console.log('Notification permission granted.');
// TODO(developer): Retrieve an Instance ID token for use with FCM.
messaging.getToken()
.then(function(currentToken) {
if (currentToken) {
console.log('Token: ' + currentToken)
sendTokenToServer(currentToken);
// updateSubscriptionOnServer(currentToken);
} else {
console.log('No Instance ID token available. Request permission to generate one.');
setTokenSentToServer(false);
}
})
......
The pushManagr.js is included in both login/index.html and index.html.
I think that calling navigator.serviceWorker.register at multiple places will have adverse effect.
so how I can combine the two instances into one.
If possible, remove the registration of the serviceworker from pushManager.
If you want the registration instance of the serviceworker,use the api getRegistration()
navigator.serviceWorker.getRegistration(/*scope*/).then(function(registration) {
if(registration){
// Move all your firebase messaging code to here
messaging.useServiceWorker(registration);
}
});

Client side functions doesn't call

I had lot of difficulties after upgrading signalR & .NET version.
Previously I had 1.XX version now I have 2.4.0 signal R version.
This question is directly connected with - https://github.com/SignalR/SignalR/issues/4339
But after upgrade signal R doesn't work.
Now the problem is client-side functions cannot call.
I just tried this: Signalr doesn't call client side functions
and fixed it according to the correct answer:
In your init prior to $.connection.hub.start call your _subscribe method.
Later I went through a bit deeper down on this issue, and added console.log below place in my signalr.js
connection.socket.onmessage = function (event) {
var data;
try {
console.log(event.data);
data = connection._parseResponse(event.data);
}
catch (error) {
transportLogic.handleParseFailure(connection, event.data, error, onFailed, event);
console.log("socket error" + event.data);
return;
}
if (data) {
transportLogic.processMessages(connection, data, onSuccess);
}
};
After every one joins meeting -> meeting start and ask for vote (this place we should call signalR)
From vote asking person side I see console log like this:
Normal user ( voting persons console log looks like this:
This is from Firefox - another user:
I think it already triggering - client hub event 'sendOnlineMeetingVoteRequest' on hub 'NotificationHub'.
It already hit server-side function too but the thing is it never hits this part of the code:
notificationHub.client.sendOnlineMeetingVoteRequest = function (token, meetingId, meetingVoteId) {
debugger;
if (token == '#Model.Organization' && '#Model.MeetingId' == meetingId) {
ShowMeetingOnlineMeetingVotePopup(meetingId, meetingVoteId);
}
};
I went through http://localhost:33852/signalr/hubs
/*!
* ASP.NET SignalR JavaScript Library v2.3.0-rtm
* http://signalr.net/
*
* Copyright (c) .NET Foundation. All rights reserved.
* Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
*
*/
/// <reference path="..\..\SignalR.Client.JS\Scripts\jquery-1.6.4.js" />
/// <reference path="jquery.signalR.js" />
(function ($, window, undefined) {
/// <param name="$" type="jQuery" />
"use strict";
if (typeof ($.signalR) !== "function") {
throw new Error("SignalR: SignalR is not loaded. Please ensure jquery.signalR-x.js is referenced before ~/signalr/js.");
}
var signalR = $.signalR;
function makeProxyCallback(hub, callback) {
return function () {
// Call the client hub method
callback.apply(hub, $.makeArray(arguments));
};
}
function registerHubProxies(instance, shouldSubscribe) {
var key, hub, memberKey, memberValue, subscriptionMethod;
for (key in instance) {
if (instance.hasOwnProperty(key)) {
hub = instance[key];
if (!(hub.hubName)) {
// Not a client hub
continue;
}
if (shouldSubscribe) {
// We want to subscribe to the hub events
subscriptionMethod = hub.on;
} else {
// We want to unsubscribe from the hub events
subscriptionMethod = hub.off;
}
// Loop through all members on the hub and find client hub functions to subscribe/unsubscribe
for (memberKey in hub.client) {
if (hub.client.hasOwnProperty(memberKey)) {
memberValue = hub.client[memberKey];
if (!$.isFunction(memberValue)) {
// Not a client hub function
continue;
}
// Use the actual user-provided callback as the "identity" value for the registration.
subscriptionMethod.call(hub, memberKey, makeProxyCallback(hub, memberValue), memberValue);
}
}
}
}
}
$.hubConnection.prototype.createHubProxies = function () {
var proxies = {};
this.starting(function () {
// Register the hub proxies as subscribed
// (instance, shouldSubscribe)
registerHubProxies(proxies, true);
this._registerSubscribedHubs();
}).disconnected(function () {
// Unsubscribe all hub proxies when we "disconnect". This is to ensure that we do not re-add functional call backs.
// (instance, shouldSubscribe)
registerHubProxies(proxies, false);
});
proxies['NotificationHub'] = this.createHubProxy('NotificationHub');
proxies['NotificationHub'].client = { };
proxies['NotificationHub'].server = {
sendMeetingStartMessage: function (token, meetingId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendMeetingStartMessage"], $.makeArray(arguments)));
},
sendMeetingStopMessage: function (token, meetingId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendMeetingStopMessage"], $.makeArray(arguments)));
},
sendMeetingTreeRefreshRequest: function (token, meetingId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendMeetingTreeRefreshRequest"], $.makeArray(arguments)));
},
sendMessage: function (token, meetingId, agendaGroupItemId, motionId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendMessage"], $.makeArray(arguments)));
},
sendOnlineMeetingVoteCloseRequest: function (token, meetingId, meetingVoteId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendOnlineMeetingVoteCloseRequest"], $.makeArray(arguments)));
},
sendOnlineMeetingVoteRequest: function (token, meetingId, meetingVoteId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendOnlineMeetingVoteRequest"], $.makeArray(arguments)));
},
sendOnlineVoteCloseRequest: function (token, meetingId, agendaGroupItemId, motionId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendOnlineVoteCloseRequest"], $.makeArray(arguments)));
},
sendOnlineVoteRequest: function (token, meetingId, agendaGroupItemId, motionId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendOnlineVoteRequest"], $.makeArray(arguments)));
},
sendOnlineVoteResult: function (token, meetingId, agendaGroupItemId, motionId, selectedVotingOptionId) {
return proxies['NotificationHub'].invoke.apply(proxies['NotificationHub'], $.merge(["sendOnlineVoteResult"], $.makeArray(arguments)));
}
};
return proxies;
};
signalR.hub = $.hubConnection("/signalr", { useDefaultPath: false });
$.extend(signalR, signalR.hub.createHubProxies());
}(window.jQuery, window));
So I didn't found any error on it too.
Still, I cannot figure it out why this is happening but I went through the sample project that uses signalR 2.0.3.0 version
I went to reference & just noted that this reference - Microsoft.AspNet.SignalR.Owin is not included in that sample project that I downloaded.
I did some investigation furthermore & find out this:
'The call is ambiguous between the following methods or properties'
This error will occur if a reference to Microsoft.AspNet.SignalR.Owin
is not removed. This package is deprecated; the reference must be
removed and the 1.x version of the SelfHost package must be
uninstalled.
(https://learn.microsoft.com/en-us/aspnet/signalr/overview/releases/upgrading-signalr-1x-projects-to-20)
Do I need to remove that?
In my web config, there is no code like this.
I just call this function purely from another place - about us page. only add relevant script and function.
from that place, it worked.
after that, I changed some scripts placing and fix this issue.
the main thing is I didn't get any error or anything. thing looks working all the time. but because of some script placement, it doesn't work.

XML API calling not working with cordova-plugin-advanced-http latest version in ionic3

I am using cordova-plugin-advanced-http plugin for API calling and all JSON enabled API working fine but I have one XML embedded API which is working fine in Postman but while I call it from ionic its param not getting at the server end.
Below is my code for XML API:
Type 1:
let headers = {
"Content-type": 'text/xml; charset=utf-8',
"Authorization": token,
};
let xmlBody =
'<ServiceRequest>' +
'<CaseNumber>' + caseNumber +
'</CaseNumber>' +
'</ServiceRequest>'
this.httpPlugin.setDataSerializer('utf8');
this.httpPlugin.post('https://test.com/Service', xmlBody, headers).then((response) => {
console.log("XML Response : ", JSON.stringify(response.data));
xml2js.parseString(response.data, function (err, result) {
console.log("XML parser success:", result);
console.log("XML parser error:", err);
if (result) {
resolve(result);
} else {
reject(err);
}
});
}).catch(error => {
if (error.status == 403) {
console.log("Token expired : " + JSON.stringify(error));
} else {
console.log("Error : " + error.error);
console.log("Error " + JSON.stringify(error));
reject(error);
}
});
Type 2:
let xmlBody = '<ServiceRequest>' +
'<CaseNumber>' + caseNumber +
'</CaseNumber>' +
'</ServiceRequest>';
console.log("XML Body", xmlBody)
// this.httpPlugin.setRequestTimeout(60);
this.httpPlugin.setDataSerializer('utf8');
this.httpPlugin.setHeader('*', 'authorization', token);
this.httpPlugin.setHeader('*', 'Content-Type', "application/x-www-form-urlencoded");
this.httpPlugin.post('https://test.com/Service', xmlBody, {}).then((response) => {
console.log("XML Response : ", JSON.stringify(response.data));
xml2js.parseString(response.data, function (err, result) {
console.log("XML parser success:", result);
console.log("XML parser error:", err);
if (result) {
resolve(result);
} else {
reject(err);
}
});
}).catch(error => {
if (error.status == 403) {
console.log("Token expired : " + JSON.stringify(error));
} else {
console.log("Error : " + error.error);
console.log("Error " + JSON.stringify(error));
reject(error);
}
});
All the time it's throwing errors from the server and with the same request, I am able to get data in postman as well as Native iOS code.
I referred this issue on GitHub but still no success.
Something I am missing though it's not able to get data on the server.
Help me to solve this issue.
After struggling a lot on this issue I found a solution to clean my request cookies.
In the HTTP Advanced plugin, there is one method to clear my cookies.
clearCookies()
Clear all cookies.
Use this method before calling any API.
So what it will do clear all my cookies and my issue related to old cookies will be solved in this way.
constructor(
public storage: Storage,
public httpPlugin: HTTP,
private platform: Platform
) {
// enable SSL pinning
httpPlugin.setSSLCertMode("pinned");
//Clear old cookies
httpPlugin.clearCookies();
}
The above code solves my issue.
Thanks all for your quick guidance and suggestions.
comment on this if this is not the right way to clear my old request data.

Service Worker, double caching?

Im having trouble with my Service Worker. I have implemented it with the Cache then Network technique, where content is first fetched from cache, and a network-fetch is always performed and the result is cached at success. (Inspired by this solution, CSS-Tricks)
When I make changes to my web app and hit refresh, I of course, the first time get the old content. But on subsequent refreshes the content alternates between old and new. I can get new or old content five times in a row or it could differ on each request.
I have been debugging the Service Worker for a while now and does not get any wiser. Does anyone have an idea about whats wrong with the implementation?
EDIT:
var version = 'v1::2';
self.addEventListener("install", function (event) {
event.waitUntil(
caches
.open(version + 'fundamentals')
.then(function (cache) {
return cache.addAll([
"/"
]);
})
);
});
self.addEventListener("fetch", function (event) {
if (event.request.method !== 'GET') {
return;
}
event.respondWith(
caches
.match(event.request)
.then(function (cached) {
var networked = fetch(event.request)
.then(fetchedFromNetwork, unableToResolve)
.catch(unableToResolve);
return cached || networked;
function fetchedFromNetwork(response) {
var cacheCopy = response.clone();
caches
.open(version + 'pages')
.then(function add(cache) {
cache.put(event.request, cacheCopy);
});
return response;
}
function unableToResolve() {
return new Response('<h1>Service Unavailable</h1>', {
status: 503,
statusText: 'Service Unavailable',
headers: new Headers({
'Content-Type': 'text/html'
})
});
}
})
);
});
self.addEventListener("activate", function (event) {
event.waitUntil(
caches
.keys()
.then(function (keys) {
return Promise.all(
keys
.filter(function (key) {
return !key.startsWith(version);
})
.map(function (key) {
return caches.delete(key);
})
);
})
);
});
I don't see how you are setting the version, but I presume multiple caches still exist (I can see you are trying to delete the previous caches but still). caches.match() is a convenience method and the order is not guaranteed (at least Chrome seems to query the oldest one first). Chrome Developer Tools shows you the existing caches (Application/Cache/Cache Storage) and their contents. If you want to query a specific cache, you'll need to do:
caches.open(currentCacheName).then(function(cache) {...}
as in the example in the Cache documentation.

409 http error in Couchbase lite in phonegap on iOS

I'm getting a 409 on PUT, POST and DELETE actions.
I have successfully created a database and have PUT one document successfully ONCE. I have tried local and "normal" documents. I haven't spend any focus on revisions but think it has to do with this. I only want to save and update this one JSON string in my app - thats it.
It's like I have created this one document to stay forever :-)
Will sample code help? I'm really only using Angular's $http.
On a side note: I need a save mechanism in phonegap that is html5 cache-clear resistent.
You need to check if your document exists first before you update it. that's why it worked the first time and not the second.
config.db.get( "myDocumentID", function(error, doc) {
if (error) {
if (error.status == 404) {
//document does not exist insert it
config.db.put( "myDocumentID", myDocument, function(error,ok) {
if(error) { alert( "error" + JSON.stringify( error ) } else {
alert("success");
}
} )
} else {
alert( "error:" + JSON.stringify( error ) )
}
} else {
//update your document
doc.my_new_key = "value";
config.db.put( "myDocumentID", doc, function(error, ok) {
if( error ) { alert( "error:" + JSON.stringify( error ) ) } else {
alert("success");
}
} );
}
} )

Resources