MobileServiceClient.invokeApi results in "No action was found on the controller" - azure-mobile-services

I have custom API named Foo. When I'm trying to invoke this custom method from a Html page using javascript library of MobileServiceClient I get error:
"No action was found on the controller 'Foo' that matches the request"
<script type="text/javascript" src="http://ajax.aspnetcdn.com/ajax/mobileservices/MobileServices.Web-1.2.7.min.js"></script>
< script type = "text/javascript" >
$(document).ready(function() {
$('#runAllTests').on('click', function() {
var serviceClient = new WindowsAzure.MobileServiceClient('<url>', '<key>');
serviceClient.invokeApi('Foo', {
body: {
val1: 'value1',
val2: 'value2',
},
method: 'GET'
}).done(function(results) {
console.log('success');
console.log(results);
}, function(error) {
console.log('error');
console.log(error);
});
});
}); < /script>
However when I invoke this method from my Windows Store App, everything works fine.
The cross-origin resource sharing (cors) configured to *
UPDATE: It looks like I need to call this method specifying the parameters with the method name like:
serviceClient.invokeApi('Foo?val1=value1&val2=value2', {
method: 'GET'
})
Is it correct? Looks like even for the POST method I have to do the same unless my parameters have a complex type.
UPDATE 2: the definition of my custom API method as follows
[AuthorizeLevel(AuthorizationLevel.Anonymous)]
public class FooController : ApiController
{
public HttpResponseMessage Get(string val1, string val2)
{
//method body
}
}

Ok, I've found out that I need to pass these parameters not via body but via parameters. When you pass them via body or there is at least one of the parameters missing than you will get the 'Not found' error.
serviceClient.invokeApi('Foo', {
parameters: {
val1: 'value1',
val2: 'value2',
},
method: 'GET'
})

Related

Typescript - Get uninitialized properties after compilation

I am currently writing a wrapper around socket.io. Comming from a very object-oriented background, I want to implement the concept of Models in my framework/wrapper.
If you happen to know socket.io you might know that you get the data that is associated with an event as a parameter, now I have implemented a custom routing system where the handler of the route gets the data in an express.js like request object.
The idea is to have model classes that look something like this:
class XRequestModel
#v.String({ message: 'The username must be a string!' })
public userName: string;
}
And the route event might look something like this:
#RouteConfig({ route: '/something', model: XRequestModel })
class XEvent extends Route {
public on(req: Request<XRequestModel>, res: Response) {
// Handle Event
}
}
And to complete the example here is how the request object might look like:
class Request<T> {
public data: T;
}
Now generics in typescript are very limited since the type information is removed after compilation, I can not use the generic Request parameter ( which is the type of the model ) to get metadata from the model - Metadata, in this case, is the validation decorator. To overcome this issue I give a reference of the Model class to the RouteConfig of the RouteEvent, which is internally used and would allow me to create instances of the model, get the properties and so on...
The idea here is to give the handler of a route, a request object with pre-validated, typesafe data.
The thing holding me back from this, is the fact that unused properties, get removed after compilation by typescript, So I cannot get the metadata of the model. Initializing the class-property would solve this:
class XRequestModel
#v.String({ message: 'The username must be a string!' })
public userName: string = '';
}
But I think this makes for some very verbose syntax, and I dont want to force the user of this wrapper to init all the model properties.
An implementation side-note:
The user of the framework has to register the classes to a 'main' class and from there I can get the Route-class via decorator reflection.
When I try to get the properties of the model without initialized properties - First model example.
// Here the route.config.model refers to the model from the RouteConfig
Object.getOwnPropertyNames(new route.config.model());
>>> []
Here is what I get with initialized properties:
Object.getOwnPropertyNames(new route.config.model());
>>> [ 'userName' ]
Here a link to the GitHub repository: https://github.com/FetzenRndy/SRocket
Note that models are not implemented in this repo yet.
Basically, my question is: How can I get the properties of a class that has uninitialized properties after compilation.
The problem is that if no initialization happens, no code is emitted for the fields, so at runtime the field does not exist on the object until a value is assigned to it.
The simplest solution would be to initialize all fields even if you do so with just null :
class XRequestModel {
public userName: string = null;
public name: string = null;
}
var keys = Object.getOwnPropertyNames(new XRequestModel())
console.log(keys); // [ 'userName', 'name' ]
If this is not a workable solution for you, you can create a decorator that adds to a static field on the class and the walk up the prototype chain to get all fields:
function Prop(): PropertyDecorator {
return (target: Object, propertyKey: string): void => {
let props: string[]
if (target.hasOwnProperty("__props__")) {
props = (target as any)["__props__"];
} else {
props = (target as any)["__props__"] = [];
}
props.push(propertyKey);
};
}
class XRequestModelBase {
#Prop()
public baseName: string;
}
class XRequestModel extends XRequestModelBase {
#Prop()
public userName: string;
#Prop()
public name: string;
}
function getAllProps(cls: new (...args: any[]) => any) : string[] {
let result: string[] = [];
let prototype = cls.prototype;
while(prototype != null) {
let props: string[] = prototype["__props__"];
if(props){
result.push(...props);
}
prototype = Object.getPrototypeOf(prototype);
}
return result;
}
var keys = getAllProps(XRequestModel);
console.log(keys);

How to use type check Loopback / Fireloop PersistedModel

I'm using Fireloop with Loopback 3 and wanting to know how best to create typesafe hooks and remote methods using type checked PersistedModel and Validatable methods. I'd like to change the type of the constructor from ...
constructor(public model: any) { }
to ...
constructor(public model: SomeType) { }
I'd like to make PersistedModel calls like
this.model.count().then((n) => ...);
OR Validatable calls like:
model.validatesLengthOf('code', {
min: 6, max: 12, message: { min: 'too short', max: 'too long'}
});
The Fireloop examples like the one below only use any as type of this.model.
The firestarter model samples and Fireloop documentation were also of no use here.
I know that there is a type called ModelConstructor declared in the fireloop source tree under core/index.d.ts. This interface looks correct because it implements all the PersistedModel and Validatable methods but where is it published in npmjs? Is it already part of the Fireloop server SDK or do I need to npm install it? No idea.
import { Model } from '#mean-expert/model';
/**
* #module Account
* #description
* Write a useful Account Model description.
* Register hooks and remote methods within the
* Model Decorator
**/
#Model({
hooks: {
beforeSave: { name: 'before save', type: 'operation' }
},
remotes: {
myRemote: {
returns: { arg: 'result', type: 'array' },
http: { path: '/my-remote', verb: 'get' }
}
}
})
class Account {
// LoopBack model instance is injected in constructor
constructor(public model: any) { }
// Example Operation Hook
beforeSave(ctx: any, next: Function): void {
console.log('Account: Before Save', ctx.instance);
next();
}
// Example Remote Method
myRemote(next: Function): void {
this.model.find(next);
}
}
module.exports = Account;
Finially, I've also attempted to use the Loopback 3 Typescript definitions but hit more problems as the PersistedModel methods here are all declared as static so fail type checks and return Promise<T> | void. The later means you’re forced to type cast the result back to just Promise<T> so it seems like the type def authors have never actually used them. Is this a bug or am I missing something? Can't find any working examples to prove otherwise.
This is the server side API pain. Client side REST API for Fireloop is also undocumented (lots of example for Real-time API) but none for the REST api it's also supposed to include (just mentioned once in one issue). Would be nice to find it can all be type checked by Typescript.
I found that ModelConstructor validation methods were missing arguments like { message: 'my error message' } and methods like exists() returned Promise<any> instead of Promise<boolean>.
The type definitions in Loopback 3 Type definitions were more complete but were unusable unless fixed as described above.
In the end I used ..
# Used modified type defs from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/loopback/index.d.ts
import { Validatable } from '../types/validatable';
# Fixed static methods and return types.
import { PersistedModel } from '../types/loopback';
constructor(public MyModel: Validatable | PersistedModel) {
let Model = MyModel as Validatable;
Model.validatesLengthOf('code', {
min: 6,
max: 80,
message: { min: 'too short', max: 'too long' } });
Model.validatesFormatOf('email',
{ with: this.reEmail,
message: 'invalid email address',
allowNull: false });
Model.validatesInclusionOf('role', {
in: RoleNames,
message: 'is not valid'
});
}
And in later methods ..
let Model = this.MyModel as PersistedModel;
// knows that isFound is boolean
Model.exists(code).then(isFound => { ... });

Grails rest-api app to handle multiple params

Using Grails 3.1.3, I created a rest-api so that I am able to capture GET requests that not only query for one parameter, but multiple if needed. I don't know how to code this correctly inside the UrlMappings file. Here are the details.
Domain class:
class ProdDetail {
Integer pid
String title
String category
Integer year
}
And some of these inside the BootStrap:
new ProdDetail(pid:'101', title:'No Highway', author:'Nevil Shute', category:'fiction', year:1948).save(failOnError:true)
new ProdDetail(pid:'214', title:'In the Country of Men', author:'Hisham Matar', category:'misery', year:2007).save(failOnError:true)
Controller:
protected List<ProdDetail> listAllResources(Map params) {
println params
try {
ProdDetail.where {
if (params.category && params.maxYear) {
category == params.category && year <= params.int('maxYear')
} else if (params.category) {
category == params.category
} else if (params.maxYear) {
year <= params.int('maxYear')
} else {
pid > 0
}
}.list()
} catch (Exception e) {
[]
}
}
UrlMappings:
static mappings = {
"/prodDetails"(resources:'prodDetail')
"/prodDetails/category/$category?"(controller:'prodDetail', action:'index')
"/prodDetails/yearUnder/$maxYear?"(controller:'prodDetail', action:'index')
// the line below is not right I think, what's the correct format?
"/prodDetails/combo/$category?&$maxYear?"(controller:'prodDetail', action:'index')
}
Now, where as these two curls would work:
curl localhost:8080/prodDetails/category/misery
curl localhost:8080/prodDetails/yearUnder/2007
This one fails to go into the desired clause in the controller to detect both params:
curl localhost:8080/prodDetails/combo/?category=misery&maxYear=2007
It just detects 'category' but not the 'maxYear' which it considers as 'null'.
How can I cater for such a curl please?
It kind of depends on what you want your URLs to look like, but assuming you want your requests to look like this:
http://localhost:8080/prodDetails/combo/misery?maxYear=2007&title=common
The UrlMappings should look like
static mappings = {
"/prodDetails/combo/$category"(controller:'prodDetail', action:'index')
}
Then the params object in the controller should have both whatever's in the place of $category, in this example misery, and the other parameters after the ? as well.
If you want the parameters to be in the path you can do this:
static mappings = {
"/prodDetails/combo/$category/$title/$maxYear"(controller:'prodDetail', action:'index')
}
And the request would then be:
http://localhost:8080/prodDetails/combo/misery/common/2007
One other option would be to use a command object. So if you had:
static mappings = {
"/prodDetails/combosearch"(controller:'prodDetail', action:'comboSearch')
}
And then created an object beside the controller called ComboSearchCommand.groovy that looked like:
import grails.validation.Validateable
class ComboSearchCommand implements Validetable {
String category
String title
int maxYear
static constraints = {
category blank: false, nullable: true
title blank: false, nullable: true
maxYear blank: false, nullable: true
}
}
(Which you can do validation on just like a domain object)
And then in your controller you have the method take the command object instead of params
protected List<ProdDetail> comboSearch(ComboSearchCommand command) {
println command.category
}
Then your URL would be
http://localhost:8080/prodDetails/combosearch?category=misery&maxYear=2007&title=common
And the parameters will bind to the command object.
I've used that quite a bit, you can share validations or have your command object inherit validations from domain objects, lots of flexibility.
https://grails.github.io/grails-doc/latest/guide/single.html#commandObjects
You don't need to specify the parameters in UrlMappings if those params are not part of the URL:
No need of this:
"/prodDetails/combo/$category&?$maxYear?"(controller:'prodDetail', action:'index')
Yes you need this to match the URL to a controller/action (but remove the ?)
"/prodDetails/yearUnder/$maxYear?"(controller:'prodDetail', action:'index')
Also, you don't need Map params in listAllResources(Map params)
"params" is an injected property of controllers, the println params will work OK with: listAllResources()
What I would do is to define:
listAllResources(String category, int maxYear, ...) where ... are all the params that action can receive, most would be optional, so you will receive a null value if not included in your request.
Remember: UrlMappings are to map URLs to controller/actions, and you have the same controller/action, so I would remove all the mappings and process the optional parameters in the action just checking which are null or not.
Edit (considering comments)
Q: the method is not overloaded to handle params like that
A: methods are dynamic, this is Grails / Groovy, not Java. It will call the action method even if all the params are null. I would recommend you to go through the Grails controller documentation in detail.
Q: found that the listAllResources method was never called
A: remove the protected keyword from the action, only subclasses would be able to invoke that method. Also, you can add an UrlMapping to avoid users to invoke that URL (match the URL and return 404 Not Available or something like that)
Q: I want to handle a GET request like this localhost:8080/prodDetails/combo?category=misery&year=2016&title=commonTitle, how exactly should the i) entry in UrlMappings, and ii) the listAllResources method look like?
A:
static mappings = {
// if "compo" comes in the action portion, map that to the listAllResources method
// as I said, if all parameters comes in the query string, no further actions are needed, if you need parameters to be part of the URL path, then you need to play with the $xxxx in the URL
"/prodDetails/combo"(controller:'prodDetail', action:'listAllResources')
}
def listAllResources()
{
println params
// logic here
render "OK"
}
Check:
https://grails.github.io/grails-doc/latest/ref/Controllers/params.html
https://grails.github.io/grails-doc/latest/ref/Controllers/render.html
How does grails pass arguments to controller methods?

Laravel REST redirect from GET to POST method in SAME controller not working

I am trying to support the use of EITHER GET or POST methods in my REST controller in laravel.
So, I would like to redirect ANY get requests sent to our REST controller to the POST method in the SAME controller instead.
I have tried many things, and now have returned back to basics as follows:
routes.php
Route::resource('user', 'userController');
userController.php
class userController extends \BaseController {
public function index() {
return Redirect::action('userController#store');
}
public function store() {
echo 'yeeha!';
}
}
Performing a POST on the page works and outputs:
yeeha!
Performing a GET on the page produces:
Could not get any response
This seems to be like an error connecting to https://www.test.com/user. The response status was 0.
Check out the W3C XMLHttpRequest Level 2 spec for more details about when this happens.
I have tried many different redirects and none are successful.
The correct way is to do it is to use the routes file and just define it;
Routes.php
Route::get('/user', array ('as' => 'user.index', 'uses' => userController#store))
Route::post('/user', array ('as' => 'user.create', 'uses' => userController#store))
Controller
class userController extends \BaseController {
public function store() {
echo 'yeeha!';
}
}

Angular.js Dynamic Binding when posting to restful server

I am somewhat confused of the way to achieve the two-way data binding when posting to my server.
I defined my resource like this:
angular.module('todoServices', ['ngResource']).
factory('Todo', function($resource){
return $resource('api/v1-0/todos/:todoId', {}, {
query: {method: 'GET', params:{todoId:''}, isArray: true},
save: {method: 'POST', isArray: true}
});
})
and I pass the Todo resource to my controller as a dependency.
Then in my controller I have a method to add a new Todo item to my list:
$scope.addTodo = function() {
var savedModel = new Todo();
savedModel.title = $scope.title;
savedModel.description = $scope.description,
//...
savedModel.$save();
$scope.todos.push(savedModel);
}
This works as far as my todo appears in the list, the call to the server works and the item is added in my database.
However, since when I push it to my list, it does not have an ID, yet. The ID is generated by an auto-increment ID in my MySQL database.
My server returns the object in JSON format, so I assume, I have to specify some sort of callback function to get the data-binding to work?
What exactly do I need to do, so my todo which has been added is updated with the correct ID once my server returns the data?
Simply assign the returned object to the savedModel object. Since calls to resources are asynchronous and return a promise, you should use the success function this way:
savedModel.$save(
function success(savedModel) {
$scope.todos.push(savedModel);
});
By the way, check the isArray property of the save method, normally should be false.