Mock PUT request using ngResource - angularjs

The declaration of the User resource would be something like:
factory('User', function($resource) {
return $resource('/api/user/:userId.json', {}, {
put: {method:'PUT', params: {userId:'#id'}},
});
})
As you can see the -default- parameter for the PUT method is the id attribute within the resource.
if you would like to test:
httpBackend.expectPUT('api/user/1.json').respond(200);
userResource.put();
httpBackend.flush();
I keep getting a failure in the test cause the actual URL that it's being generated is: 'api/user/.json'. The id attribute is not being included in the URL.
It makes sense because I haven't specified the id attribute to the mock object, I didn't because I don't know how to do it.
Thanks in advance.

The path should start with '/', and you need to pass in an ID to make the path match with what is generated in your code. The URL match is string match, so you need to guarantee the URL you expect to hit is exactly same as what is generated.
httpBackend.expectPUT('/api/user/1.json').respond(200);
userResource.put({id:1});
httpBackend.flush();

Related

ngResource query with composite key parameter

I have a resource, Answer, which has a composite key made of QuestionnaireId and QuestionId. The ngResource code is as follows:
function answerResource($resource) {
return $resource("/api/answers/:questionnaireId/:questionId",
{
questionnaireId: "#questionnaireId",
questionId: "#questionId"
}
);
}
I want to query this resource with the the questionnaire Id and get back all the answers. If I use:
answerResource.query(
{
questionnaireId: questionnaireId
}
);
Then the requested url is:
/api/answers/123
When I want it to be:
/api/answers?questionnaireId=123
Otherwise I have two routes that I need to handle for the query search - one with the Id in the querystring, the other with the Id as part of the url path. (I also have queries with search text where the questionnaire Id might not be present, that would use urls like /api/answers?q=sometext).
Surely any .query parameters should be passed as querystrings, not as part of the route. How do I get the desired behaviour?
The best option I can come up with, is to create a new search method on the resource, which doesn't have the parameters in the url:
// normal resource definition here...
,{
search: {
method: "GET",
url: "/api/answers",
isArray: true
}
}
Calling that with the composite key values will append them as querystring parameters and not as part of the url, e.g. /api/answers?questionnaireId=123
Faced a similar issue, but I'm messing with composite key (item, sequence).
1- html.erb: Borrowed option 2 from https://spin.atomicobject.com/2013/11/22/pass-rails-data-angularjs/
That way I could grab inside the .js script the item from the .erb form I already have (I'm doing some kind of "manage details -- crud" on request with angularjs)
2- in the controller .js file I grab the value from the custom data in the html element using document.getElementById and getAttribute, concatenating that to the request: '/items?personId='+div.getAttribute("data-personId")+'&format=json'
3- in the items_controller index action, by default rails g expects no argument and grabs all items, then I had to ask
if params[:personId].present?
#items = Item.where(personId: params[:personId])
else
grab-all-items
end
This works, but I'm not sure if it's the best approach because I'm a real newbie for angularjs.
Just messing with this issue, cannot figure yet how to remove a record.
$resource crete path instead of querystring because you define placeholders :
return $resource("/api/answers/:questionnaireId/:questionId"..
If you delete them from resource-level and then call $resource.query({param: value}) you can make querystring, also you can specify more actions for single $resource to perform different request overriding action url property.
I created a simple (and very fast) example might help you understand:
working example :https://jsfiddle.net/Nedev_so/b71feyc6/19/
EDIT :
after your comment i understand what you need, so
resource factory :
//only for example purpose
var answersBaseUrl = "https://example.com/api/answers";
var answerTemplateUri = commentsBaseUrl + '/:questionnaireId/:questionId'
var params = {questionnaireId: '#_questionnaireId',questionId: '#_questionId'};
var res = $resource(answerTemplateUri, params,{
one :{
method: "GET",
},
all: {
method: "GET",
url: answersBaseUrl,
isArray: true
}
});
return res;
});
and then in your controller :
//get answers by questionnaire
//GET /answers?questionnaireId=123
$scope.answersByQuestionnaire = answers.all({questionnaireId: 123});
//get answers by question
//GET /answers?questionId=123
$scope.answersByQuestion = answers.all({questionId:123});
//get single answer by questionnaire and question
//GET /answers/123/123
$scope.answer = answers.one({questionnaireId: 123,questionId:123 });
(check network logs and you can see expected behaviour)

Set path parameter without affecting payload using $resource

First of all I want to mention that I have been digging around a lot for this. I am unable to find a simple and straight forward answer even in the docs. (Call me dumb if you will, in case it IS mentioned in the docs! I can't seem to find it anyway.)
The thing is, I want to make a PUT request to a URL of the form
app.constant('URL_REL_VENDOR_PRODUCTS', '/api/vendor/:vendorId/products/:productId');
But I do not want to put the vendorId parameter in the request payload. My service layer looks something like this:
services.factory('VendorProductService', function($resource, UserAccountService, URL_BASE, URL_REL_VENDOR_PRODUCTS) {
return $resource(URL_BASE + URL_REL_VENDOR_PRODUCTS, {
vendorId: UserAccountService.getUser().vendorId,
id: '#id'
}, {
update: { method: 'PUT' }
});
});
I know that instead of the vendorId: UserAccountService.getUser().vendorId I could have written something along the lines vendorId: '#vendorId' but then that pollutes my payload doesn't it?
I don't want to keep the mechanism I am already using in the example as the mechanism does not work when you switch accounts i.e.,if the UserAccountService.getUser() is updated. Basically I'm having to reload the entire page to get the service initialized again.
In short, the question is, as the title suggests, how do I set the path parameter vendorId without using a service like the one in the snippet and also without modifying the payload?
Make the parameter value a function:
services.factory('VendorProductService', function($resource, UserAccountService, URL_BASE, URL_REL_VENDOR_PRODUCTS) {
return $resource(URL_BASE + URL_REL_VENDOR_PRODUCTS, {
vendorId: function () {
return UserAccountService.getUser().vendorId
},
id: '#id'
}, {
update: { method: 'PUT' }
});
});
From the Docs:
paramDefaults (optional)
Default values for url parameters. These can be overridden in actions methods. If a parameter value is a function, it will be executed every time when a param value needs to be obtained for a request (unless the param was overridden).
-- AngularJS $resource API Reference

AngularJS | Set Path Parmeter while using $http.get method

I have a GET endpoint with URI as /user/user-id . The 'user-id' is the path variable here.
How can I set the path variable while making the GET request?
This is what I tried:-
$http.get('/user/:id',{
params: {id:key}
});
Instead of replacing the path variable, the id get appended as query param.
i.e my debugger show the request URL as 'http://localhost:8080/user/:id?id=test'
My expected resolved URL should be like 'http://localhost:8080/user/test'
$http's params object is meant for query strings, so key-value pairs you pass into params are output as query string keys and values.
$http.get('/user', {
params: { id: "test" }
});
Becomes: http://localhost:8080/user?id=test
If you need http://localhost:8080/user/test, you can either:
Construct the url yourself,
$http.get('/user/' + id);
Or, use $resource (specifically $resource.get https://docs.angularjs.org/api/ngResource/service/$resource). This is a little cleaner.
Why not something like this?:
var path = 'test';
$http.get('/user/' + path, {});

Testing for whether an object is an angularJS $resource

Simple (seeming) question - I'm trying to do a simple sanity check in my AngularJS controller to make sure that my $resource is actually instantiated as such. It's a largish app, but for example:
.factory('AccountSearchService_XHR', ["$resource", function($resource) {
var baseUrl = "http://localhost\\:8081/api/:version/accounts/:accountNumber";
return $resource(baseUrl,
{
version: "#version",
accountNumber: "#accountNumber"
},
{
get: {method: 'GET', isArray: false}
});
}]);
Then later, in controller:
$scope.accountObj.currentAccount = AccountSearchService_XHR.get({
version: "v1",
accountNumber: "1234"
},
function(result) {... etc etc});
The call to my API works fine, everything returns data like I expect - but I'd like to test to see if $scope.accountObj.currentAccount is a Resource before trying to make the .get call (notice the super important capital "R").
When I inspect the object $scope.accountObj.currentAccount in chrome debugger, it looks like:
Resource {accountHolderName: Object, socialSecurityNumer: null, birthDate: "05/14/1965", maritalStatus: ...}
Because of some complexity in my setup though, occasionally it gets overwritten as a normal object (typeof returns "object"), but inspecting it in debugger confirms it lost its Resource status.
So - does anyone know of a way to test whether it is a $resource? Almost like typeof $scope.accountObj.currentAccount returns "Resource"? Or perhaps a better best practices way to ensure that things are connecting up all proper and respectable-like?
All the SO articles I have seen when searching revolve around actual Jasmine testing.
Thanks in advance.
#tengen you need to have injected the type you want to check against, instead of $resource.
All resources are instances of the "class" "Resource", but that's a function that's defined inside of the factory method of the $resource service, so you have no outside visibility to use it with the instanceof operator.
However, you're wrapping that $resource creating with your own custom type, AccountSearchService_XHR, and that's what you need to make the check against.
You need AccountSearchService_XHR to be injected in your code and then perform myRef instanceof AccountSearchService_XHR and that will be === true.
Digging up an old question my intern just had. The simple solution is:
if ($scope.accountObj.currentAccount instanceof AccountSearchService_XHR)
return 'This is a AccountSearchService_XHR Resource';
else
return 'This is not a AccountSearchService_XHR Resource';
which with proper names (Users being a $resource) and real case scenario should lead you to write something like this:
if (!(this.user instanceof Users))
this.user = new Users(this.user);
this.user.$update();
Check it via instanceof yourVariable === "Resource". Because Resource is an object the type will always return as an Object, but if you check that it's an instance of the Resource "class" that should work just fine.

symfony 1.4: routing based on a partial url

I am trying to do the following:
http://www.mydomain.com/Foo/json_bar
in my routing file, I want to say anything going to Foo/json_* it should go to the appropriate action in the action.class.php file
ex:
Foo/json_bar1 -> public function executeBar1
Foo/json_bar2 -> public function executeBar2
Thanks
In that case, you could probably write a routing rule like this (untested):
my_rule:
url: /Foo/json_:action/
params: { module: myModule, sf_method: json }
This, because the :action parameter is a "magic" parameter, which sets the action. (Normally you set the action parameter in the params block.
The sf_method is optional, by the way, but it sets the request format as json. That way, any exceptions will also render in JSON, and the correct headers are set for json.
The best practice to do this by the way, would be:
my_rule:
url: /Foo/:action.:sf_method
params: { module: myModule }
In that case you can write a bar1 action. Going to /Foo/bar1.html will render the HTML, and /Foo/bar1.json will render a json response. Of course you're free the replace the :sf_method with json, and set the sf_method param, like in my first example.

Resources