I have created a RESTful API that has an optional parameter passed in as a URL variable. It seems to work correctly when executing from the browser directly but when trying it in the API Explorer it lists the parameter but ignores it when executing it. I have no idea where to start to look in solving this. Any help would be greatly appreciated.
The class definition is as follows:
class actions {
/**
* LIST available Actions
*
* List all the actions that a user (or app) can choose from. The response list
* will include [name],[slug/id], and [description] attributes. If you want a more complete set of
* meta attributes for the actions then you can specify "meta=all" in the request url. For full spec of
* response please review LG_actions_list.json.
*
* #url GET /available
*
* #param $meta {#from url} Optional parameter to control the amount of meta-data passed back. Values are "none","normal", and "all"
**/
public function available ($meta="normal")
{
return "list actions (meta level set to $meta)";
}
}
In this case I can type "all" as the value of $meta in the API explorer but the response is still "list actions (meta level set to normal)".
UPDATE:
In order to clarify this behaviour I am adding the API Explorers output and the output I get when I call the service directly:
In comparison, when actually using the API I get the correct results. Typing this into Chrome:
http://[domain]/api/actions/available?meta=foobar
I get the desired output of:
"list actions (meta level set to foobar)"
There are few problems with what you are doing with the above code
Optional Parameters are better left to query string
When you add a manual route using #url no auto routes will be added for that method
Do the following to make it work.
Changed the method name to get to map it to root of the class
Turned off smart auto routing
Now in the explorer there will be two operations listed
actions.json
actions.json/{meta}
.
class actions {
/**
* LIST available Actions
*
* List all the actions that a user (or app) can choose from. The response list
* will include [name],[slug/id], and [description] attributes. If you want a more complete set of
* meta attributes for the actions then you can specify "meta=all" in the request url. For full spec of
* response please review LG_actions_list.json.
*
* #smart-auto-routing false
* #param $meta Optional parameter to control the amount of meta-data passed back. Values are "none","normal", and "all"
**/
public function get ($meta="normal")
{
return "list actions (meta level set to $meta)";
}
}
Related
I'm building a RESTFUL API with FOSRest Bundle and return data with custom headers like this:
class InvestorController extends AbstractFOSRestController
{
/**
* Retrieve a list of investors
*
* #Rest\Get("/{page}", defaults={"page"=1}, requirements={"page"="\d+"})
*
* #param Integer $page
* #param Request $request
* #param InvestorRepository $investorRepository
*
* #return Response
*/
public function getInvestorsAction($page, Request $request, InvestorRepository $investorRepository)
{
$data = $investorRepository->getInvestorsList($page);
$data = $this->getUser()->getId();
$view = $this->view($data, 200)
->setHeader('Access-Control-Allow-Origin', 'http://localhost:3000') // Remove this bit in PROD
->setHeader('Access-Control-Allow-Credentials', 'true'); // Remove this bit in PROD
return $this->handleView($view);
}
}
Now I would like to pass these headers for all of the responses in this Controller. I would like to avoid having to send them manually each time. Is there a way to set custom headers for all of the controller's responses automatically, say, in the constructor or in somewhere else?
Thank you
There is multiple ways how to do this:
First one that comes to my mind would be to configure your webserver to attach these headers to every response it sends based on URL.
Second one would be to utilize Symfony internal event system and catch kernel.response and attach the headers there. You would need to filter the response based on where is coming from -
https://symfony.com/doc/current/reference/events.html#kernel-response
The other thing would be to create your own custom handler which calls these setHeader() methods instead of doing it inside Controller. To provide this handler you can simply override handleView() method -
https://symfony.com/doc/master/bundles/FOSRestBundle/2-the-view-layer.html#custom-handler
I've used Restler v2 for years, and have finally started to work with v3. In v2, it seemed to simply be a matter of preceding the function name with get or post for it to be visible only to the specified method.
I'm still trying to understand how v3 implements the DocBLock comments to control all of this, and am simply unable to determine the correct way to make the API visible only with POST. I've been reading Routing Example, but am clearly missing something in the explanation.
I've tried variations of most everything shown on the example below as the starting point. It authenticates and works perfectly fine with GET - How do I make it visible only with POST? The error I get is typically 404 - not found, unless the DocBlock variations cause a general PHP error of some kind.
/**
* #param string $action {#from path}
* #param string $service {#from path}
*
* #return array
*/
protected function Perform ($action, $service)
{
...
}
As it turns out, a POST isn't just a POST....I was using the GET method with my browser and AJAX to test the API. When I switched AJAX to use POST, I didn't specify the datatype for the post. Once I did that, adding the post prefix worked as expected.
I want two endpoints for my custom api which are
Create custom rules in a magento cart (URL= magento.com/coupondemo/generate)
Create coupon codes for a particular rule (URL= magento.com//coupondemo/rules/:rule_id/codes)
I've followed this tutorial to and coupon codes in Magento and I have code that can create a rule in Magento too. However, I've no clue how to create the two endpoints in my custom rest api as I am only able to create one.
I have the following routes in api2.xml
<routes>
<route_collection>
<route>/coupondemo/generate</route>
<action_type>entity</action_type>
</route_collection>
<route_collection>
<route>/coupondemo/rules/:rule_id/codes</route>
<action_type>collection</action_type>
</route_collection>
</routes>
my v1.php's skeleton is as follows
<?php
/* Coupon AutoGen REST API
*
* #category CouponDemo
* #package CouponDemo_AutoGen
* #author Chuck Hudson (used with permission). For more recipes, see Chuck's book http://shop.oreilly.com/product/0636920023968.do
*/
class CouponDemo_AutoGen_Model_Api2_Coupon_Rest_Admin_V1 extends CouponDemo_AutoGen_Model_Api2_Coupon
{
/**
* Generate one or more coupon codes using the Generate Coupons rule defined in Magento.
* Expected parameters are:
* {
* 'qty': int, - number of coupon codes to instruct Magento to generate
* 'length': int, - length of each generated coupon code
* 'format': string, - alphanum (for alphanumeric codes), alpha (for alphabetical codes), and num (for numeric codes)
* }
*
* #param array $couponData
* #return string|void
*/
protected function _create($couponData)
{
}
protected function _retrieveCollection()
{
}
protected function _retrieve($couponDatas)
{
}
}
The problem is that both the routes call the _create methods in my v1.php. I want to have the first route call a custom method with an array as a parameter. So how can I do that?
I tried using this route
<!-- Call For V1.php _retrieve() -->
<route_entity_count>
<route>/coupondemo/generate</route>
<action_type>entity</action_type>
</route_entity_count>
which calls the _retrieve method but it doesn't allow the parameter to be passed in.
How should I handle the first route then?
First let me say that the new API Explorer in Restler is great. Very happy about its addition. Now, in typical fashion, let me complain about something that isn't working for me ...
The fact that Restler can return results in multiple formats is a very nice feature but I'm currently not using it (choosing to only use JSON as my return format). In the API Explorer I'd like all references to .json to not show up as this just complicates the look of the service architecture.
Here's a quick example:
class Users {
/**
* Preferences
*
* Preferences returns a dictionary of name-value pairs that provide input to applications that want to make user-specific decisions
*
* #url GET /{user_id}/preferences
**/
function preferences ($user_id , $which = 'all') {
return "$which preferences for {$user_id}";
}
/**
* GET Sensors
*
* Get a list of all sensors associated with a user.
*
* #url GET /{user_id}/sensor
**/
function sensor ($user_id) {
return "sensor";
}
/**
* GET Sensors by Type
*
* #param $user_id The user who's sensors you are interested in
* #param $type The type of sensor you want listed.
*
* #url GET /{user_id}/sensor/{type}
**/
function sensor_by_type ($user_id, $type) {
return "specific sensor";
}
/**
* ADD a Sensor
*
* #param $user_id The user who you'll be adding the sensor to
*
* #url POST /sensor
**/
function postSensor() {
return "post sensor";
}
}
In this example the API Explorer looks like this:
The basic problem I'd like to remove is remove all ".json" references as the calling structure without the optional .json works perfectly fine.
Also, for those that DO want the .json showing up there's a secondary problem of WHERE does this post-item modifier show up? In the example above you have .json attaching to the "users" element in the GET's and to the "sensor" element in the PUT. This has nothing to do with the HTTP operation but rather it seems to choose the element which immediately precedes the first variable which may not be intuitive to the user and actually isn't a requirement in Restler (at least its my impression that you can attache .json anywhere in the chain and get the desired effect).
We are using safer defaults that will work for everyone, but made it completely configurable.
if you prefer .json to be added at the end, add the following to index.php (gateway)
use Luracast\Restler\Resources;
Resources::$placeFormatExtensionBeforeDynamicParts = false;
If you prefer not to add .json extension, add the following to index.php
use Luracast\Restler\Resources;
Resources::$useFormatAsExtension = false;
Passing uid as an argument works fine with this code:
$bouts = views_get_view_result('Results', 'page_1', array($user->uid));
The key line in views_get_view_result that sets arguments is:
$view->set_arguments($args);
But what about passing date ranges?
Also, if something is specified as a filter on a view, is there a way to prorammatically alter it?
views_get_view_result:
/**
* Investigate the result of a view.
* from Drupal.org.
*
* #param string $viewname
* The name of the view to retrieve the data from.
* #param string $display_id
* The display id. On the edit page for the view in question, you'll find
* a list of displays at the left side of the control area. "Defaults"
* will be at the top of that list. Hover your cursor over the name of the
* display you want to use. A URL will appear in the status bar of your
* browser. This is usually at the bottom of the window, in the chrome.
* Everything after #views-tab- is the display ID, e.g. page_1.
* #param array $args
* Array of arguments. (no keys, just args)
* #return
* array
* An array containing an object for each view item.
* string
* If the view is not found a message is returned.
*/
function views_get_view_result($viewname, $display_id = NULL, $args = NULL) {
$view = views_get_view($viewname);
if (is_object($view)) {
if (is_array($args)) {
$view->set_arguments($args);
}
if (is_string($display_id)) {
$view->set_display($display_id);
}
else {
$view->init_display();
}
$view->pre_execute();
$view->execute();
/* print "<pre> $viewname: $display_id";
print_r(get_class_methods($view)); */
return $view->result;
}
else {
return t('View %viewname not found.', array('%viewname' => $viewname));
}
}
As for passing data ranges and given the posted function definition, you could pass date ranges to that only if the view would accept them as arguments. I'm not 100% sure, but afaik date ranges can only be defined as filters, not as arguments, which leads to your second Question:
Programmatically altering the views filter settings is possible, but a bit messy, given the rather complicated view object/array mashup structure. In your posted function above, the first line is
$view = views_get_view($viewname);
After that, $view contains the whole view object. The filter settings are defined per display, so assuming you have a view with only a default display, you will find the filter settings under
$view->display['default']->display_options['filters']
(Note the object/array notation mix - the display is a contained object of type views_display)
The 'filters' array contains one entry per filter, with varying elements depending on the filter type. For your purpose, I would suggest to create a dummy view with just the filter you are interested in, with preconfigured/hardcoded values. Using a debugger (or var_dump/print_r) you can then take a look at the filter array after view creation. From what you find there, you should be able to deduce how to inject your custom date range.
Disclaimer: Poking around in the view like this is a bit annoying and not to effective, but it works. As of yet, I have not found a concise documentation of Views2 that would explain the innards in a straight forward way, as I find the official API documentation a bit lacking concerning the usage from code. (Of course this could well be just me being to stupid ;)
If you're using views 2, you can use the GUI to add a date argument. Then in the url, you can put :
www.yousite.com/yourview/startDate--finishDate
For the startDate/finishDate, the format is YYYY-MM-DD-HH.
GL!