Using CRM WebAPI to execute GetAllTimeZonesWithDisplayName function - javascript

I'm trying to execute the GetAllTimeZonesWithDisplayName function to retrieve the current user's timezone; however I'm having some difficulties
I've been following this blog but something seems to have changed. According to the author I should be able to go:
/api/data/v8.2/GetAllTimeZonesWithDisplayName(LocaleId=1033)
but this results in an error like:
{
"error":
{
"code":"",
"message":"Resource not found for the segment 'GetAllTimeZonesWithDisplayName'.",
"innererror":{
"message":"Resource not found for the segment 'GetAllTimeZonesWithDisplayName'.",
"type":"Microsoft.OData.Core.UriParser.ODataUnrecognizedPathException"
,"stacktrace":...
}
}
}
So I had a look in the metadata (/api/data/v8.2/$metadata) and I can see that this method requires two parameters (I think)
<Function Name="GetAllTimeZonesWithDisplayName" IsBound="true">
<Parameter Name="entityset" Type="Collection(mscrm.timezonedefinition)" Nullable="false"/>
<Parameter Name="LocaleId" Type="Edm.Int32" Nullable="false"/>
<ReturnType Type="Collection(mscrm.crmbaseentity)" Nullable="false"/>
</Function>
But I don't know what is required for the entityset parameter and it doesn't seem to be listed in the MSDN documentation

The function, according to a metadata, is bound to an entityset Parameter Name="entityset" Type="Collection(mscrm.timezonedefinition)" Nullable="false"/> means you should start it from the set. And, I don't know why, usage of bound functions requires full function name so the result will be:
/api/data/v8.2/timezonedefinitions/Microsoft.Dynamics.CRM.GetAllTimeZonesWithDisplayName(LocaleId=1033)

Related

Autocomplete in Google Apps Script with data from Google Sheets

I am trying to use Google Apps Script to create a form, where the fields allow for autocompletion. In other forms I’ve created, I’ve been able to pull an array of options from a google sheet, and use them to populate a drop down list, so I have to think it’s possible to do the same with an autocomplete process.
I’ve blatantly copied this example from w3schools, and it works exactly as needed, as long as I declare the array within the javascript (as done in the example). But what I haven’t been able to figure out is how to populate the array with options pulled from my google sheet.
Here is where I started:
var PMOSupport;
$(function() {
google.script.run.withSuccessHandler(buildDropDowns)
.getPMOSupport();
});
function buildDropDowns(data) {
PMOSupport = data;
console.log(PMOSupport);
}
function autocomplete(inp, arr) {
console.log("ENTER AUTO");
var currentFocus;
inp.addEventListener("input", function(e) {
// all of the remaining code is direct from the w3schools example
// I'm cutting it from here for brevity,
// and because I know this works, when using the declared array below
});
}
var countries = ["Afghanistan","Albania","Algeria","Andorra"];
// this line works fine, when using the array declared above
// autocomplete(document.getElementById("myInput"), countries);
// this line does not work, when trying to use the array populated from the google sheet
autocomplete(document.getElementById("myInput"), PMOSupport);
When I run this, the page creates, and as soon as I type into the entry field, I get a message in the console:
`Uncaught TypeError: Cannot read property 'length' of undefined`
at HTMLInputElement.<anonymous> (<anonymous>:32:28)
When I look into this, it’s saying that the ‘arr’ argument (PMOSupport) isn’t populated. That’s why I added the 2 console.log lines, to see what order things are happening. When I open the page, “ENTER AUTO” logs first, then the State Changes from Idle to Busy and Busy to Idle (while it calls getPMOSupport()), then the PMOSupport array logs in the console (also proving that I am in fact getting the correct data back from the sheet). So clearly, it’s entering function autocomplete() before it calls the google.script.run.withSuccessHandler(buildDropDowns).getPMOSupport() portion, which is why the 'arr' argument is undefined.
I’ve tried various ways of taking that out of the $(function() … }); block, to try to get the PMOSupport array populated before the autocomplete() function runs. Nothing I’ve done seems to be working.
I’m sure this is something simple, and caused by bad habits I’ve picked up over time (I’m not a developer, I just cobble things together for my team). But any help would be appreciated.
You need to call autocomplete AFTER the asynchronous code has returned. So, you need to invoke it from the callback.
function buildDropdowns(data, userObject) {
// probably you should indicate in data which field these data is for, or use
// the userObject parameter in the google.script.run API
autocomplete(document.getElementById("myInput"), data);
}
Alternately (I haven't and won't look at the w3schools code), declare your PMOSupport as a const array initially, and then add the entries from your callback into it (instead of reassigning it). This way, the variable is not undefined, it is just empty at the start. Depending on the implementation of the autocomplete code, this may or may not work for you.
const PMOSupport = [];
....
function buildDropdowns(data) {
PMOSupport.push(...data);
// or
// Array.prototype.push.apply(PMOSupport, data);
}

There are `n` number of calls to an http api in angularjs. Should I expect them `n` number of times?

One of my angularjs files had a code construct similar to the below:
this.examplecall = function() {
var a = apiconfig.getconfig();
....
//some lines of code
....
var b = apiconfig.getconfig();
}
And I started to write unit tests for it through angular spec - Jasmine, ended up with the below stub of code.
describe("test examplecall")...
it("should test cofig call in examplecall", function() {
$httpBackend.expectGET(GET_CONFIG).respond(200);
});
The above code throws exception telling "unexpected GET.."
When I added an extra expectGET, things worked out fine. See below:
describe("test examplecall")...
it("should test cofig call in examplecall", function() {
$httpBackend.expectGET(GET_CONFIG).respond(200);
$httpBackend.expectGET(GET_CONFIG).respond(200);
});
From this, I infer that if there are two api calls in a particular function, then I have to expect it two times.
Does that mean, if there are n same api calls in a particular code stub, I have to expect them n number of times ?
Are there any similar constructs like,
$httpBackend.WheneverexpectGET(GET_CONFIG).respond(200);
so, whenever we call a API just return 200 status like above?
Thank you for your comments on this...
EDIT: (read the accepted answer before going through this.)
Thanks to #kamituel for the wonderful answer.
To summarise with the information provided in his answer:
Use of expect :
Expects the order of the API call. It expects that the code should call the api's in the exact order that you expect.
If there are 3 api calls, then you should expect them 3 times.
Use of when : ($httpBackend.when)
Does not expect if an API call is made or not. Just doesn't throw any error.
$httpBackend.when behaves like a mini mock database. Whenever your code, expects some response from an API, supply it. Thats it.
Yes, .expectGET is used to assert that a given request has been made by the application. So you need to call it n times if you expect the application to make n requests.
If you don't need to assert on that, but only want to make application logic work through any requests it makes, you might want to use .whenGET instead. Difference between .expectXXX and .whenXXX has been already described in another answer.
Edit: not sure which Angular version are you using, but you will find this in the implementation of .expectGET:
expectations.shift();
This is invoked once a request is made and matches what was expected. Which means the same expectation is only asserted on once.
It's usually also a good idea to call .verifyNoOutstandingExpectation() after your test is done, to ensure that each request you specified as epxected using .expectXXX() has indeed been made by the application.

Unresolved property variable when returned from method

...
doChunk().then(function (results) {
angular.forEach(results, function (info) {
if (info.data.fields.worklog) {
configProcess.results.push(info.data);
...
The above is just a sample from my AngularJS application, but it's the same issue for all data (and vanilla JS) that's returned from somewhere else - like an HTTP request in this case.
results - is the result of an HTTP request and contains an array of objects.
So when I loop over this array I access different properties of these objects.
All is fine and it works, but how can I declare what the different properties of these methods are?
Basically what I want is to get rid of code inspection errors from WebStorm like these: Unresolved variable fields at line 106.
It makes perfect sense to me why it's reported, but how do I address it?
I can suggest using JSDoc to document such objects received by ajax calls. See How to fight tons of unresolved variables warning in Webstorm?, for example

restangular save ignores changes to restangular object

I'm trying to call save on a restangularized object, but the save method is completely ignoring any changes made to the object, it seems to have bound the original unmodified object.
When I run this in the debugger I see that when my saveSkill method (see below) is entered right before I call save on it the skill object will reflect the changes I made to it's name and description fields. If I then do a "step into" I go into Restangular.save method. However, the 'this' variable within the restangular.save method has my old skill, with the name and description equal to whatever they were when loaded. It's ignoring the changes I made to my skill.
The only way I could see this happening is if someone called bind on the save, though I can't why rectangular would do that? My only guess is it's due to my calling $object, but I can't find much in way of documentation to confirm this.
I'm afraid I can't copy and paste, all my code examples are typed by hand so forgive any obvious syntax issues as typos. I don't know who much I need to describe so here is the shortened version, I can retype more if needed:
state('skill.detail', {
url: '/:id',
data: {pageTitle: 'Skill Detail'},
tempalte: 'template.tpl.html'
controller: 'SkillFormController',
resolve: {
isCreate: (function(){ return false;},
skill: function(SkillService, $stateParams){
return SkillService.get($stateParams.id, {"$expand": "people"}).$object;
},
});
my SkillService looks like this:
angular.module('project.skill').('SkillService', ['Restangular, function(Retangular) {
var route="skills";
var SkillService= Restangular.all(route);
SkillService.restangularize= function(element, parent) {
var skill=Restangular.restangluarizeElement(parent, elment, route);
return skill;
};
return SkillService;
}];
Inside of my template.tpl.html I have your standard text boxes bound to name and description, and a save button. The save button calls saveSkill(skill) of my SkillFormController which looks like this:
$scope.saveSkill=function(skill) {
skill.save().then(function returnedSkill) {
toaster.pop('success', "YES!", returnedSkill.name + " saved.");
...(other irrelevant stuff)
};
If it matters I have an addElementTransformer hook that runs a method calling skilll.addRestangularMethod() to add a getPeople method to all skill objects. I don't include the code since I doubt it's relevant, but if needed to I can elaborate on it.
I got this to work, though I honestly still don't know entirely why it works I know the fix I used.
First, as stated in comments restangular does bind all of it's methods to the original restangularizedObject. This usually works since it's simply aliasing the restangularied object, so long as you use that object your modifications will work.
This can be an issue with Restangular.copy() vs angular.copy. Restangualar.copy() makes sure to restangularize the copied object properly, rebinding restangualr methods to the new copy objects. If you call only Angular.copy() instead of Restangualar.copy() you will get results like mine above.
However, I was not doing any copy of the object (okay, I saved a master copy to revert to if cancel was hit, but that used Restangular.copy() and besides which wasn't being used in my simple save scenario).
As far as I can tell my problem was using the .$object call on the restangular promise. I walked through restangular enough to see it was doing some extra logic restangularizing methods after a promise returns, but I didn't get to the point of following the $object's logic. However, replacing the $object call with a then() function that did nothing but save the returned result has fixed my issues. If someone can explain how I would love to update this question, but I can't justify using work time to try to further hunt down a fixed problem even if I really would like to understand the cause better.

Monkey patch not firing. (Guess this Monkey cant punch ducks.)

I am trying to patch the JsonRest query method. Nothing I do seams to have an effect. Below I would expect the query method to no longer work and just write "monkey punching a duck." out to the console. But alas the entire app keeps on working ignoring my blatant attempt to break it. Do I need to patch a specific instance?
dojo.require("dojo.store.JsonRest");
(function(query, options){dojo.store.JsonRest.query=function(){console.info("monkey punching a duck.");};})();
aprStore = new dojo.store.JsonRest({"target":"/web/rest/apr/","idProperty":"ID"});
var sqry = "?nq=aquerytorun";
aprStore.query(sqry).then(function(result){});
The main goal is I want at the underlying xhrGet so I can attach a callback to the error property. The .query(function,function) is ignoring my error function passed in as the second parameter. http 302 is what is driving me nuts at the moment.
It's not 100% clear what you're trying to do, but I suspect you need to put your function on the prototype object of that JsonRest thing:
dojo.store.JsonRest.prototype.query = function() { ... };
Then instances made from that constructor will have access to your "query" function.

Categories

Resources