I am getting stuck creating my own app to use within Zapier. It is for an unsupported CRM
https://www.brightpearl.com/developer/latest/
I have been able to authenticate and create a test trigger - a simple call to retrive information about one product ID where the ID is provided in the request URL manually.
Example Use Case
Using an Email sent to a GMail account, search for a customerand add the body of the email as a note to the customer.
I can search using Zapier->Searches to retrieve a result.
The Brightpearl API search returns an ID for any matched contacts.
https://www.brightpearl.com/support/documentation/resource-search
The ID can be accessed in the json response
{
response: {
results: [
[
4,
"admin#email.com",
"Primary",
"Admin"
]
]
},
reference: {}
}
The ID is required to add the note later
How do I store the ID to use in the Action later?
How do I chain the events together so that the Action is called after the ID is captured?
I have gone through the Zapier documentation and cannot find example code which does this.
The trigger that exposes the ID in the API response (in this case, the Brightpearl search result) can be mapped to a subsequent action. You don't store data in Zapier - you just pass it between actions.
Zapier's multi-step interface lets you append actions which can accept any data returned from the previous step.
Related
I have recently explored Apigility I want to use HTTP DELETE method to delete some entity but before deleting I need to validate "entityId" must be given and must be Digit and trim.
Problem is documentation mentions that:
Content Validation currently only works for POST, PATCH, and PUT requests. If you need to validate query string parameters, you will need to write your own logic for those tasks.
https://apigility.org/documentation/content-validation/intro
I have make some custome modification in config file as bellow:
'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Controller' => [
'DELETE' => 'NetworkingNightAPI\\V1\\Rpc\\AssignTimeSlotToLoginUser\\Validator',
],
As I have mention DELETE method to validate same as NetworkingNightAPI\V1\Rpc\AssignTimeSlotToLoginUser\Validator but the issue is it always return 'Value could not be empty' even I have added valid row JSON values using PostMan
Thanks!
Thank you for your reply
What I have found is Apigility uses 'zf-content-validation' module for validating the input data (https://github.com/zfcampus/zf-content-validation)
This module dose not restrict such HTTP Methods you can apply validation to DELETE method as well Like it says that
"In the above example, the Application\Controller\HelloWorld\Validator service will be selected for PATCH, PUT, or DELETE requests, while the Application\Controller\HelloWorld\CreationValidatorwill be selected for POST requests."
So you just need to add manual entry for DELETE method in config file as below:
'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Controller' => [
'input_filter' => 'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Validator',
'DELETE' => 'NetworkingNightAPI\\V1\\Rpc\\DeleteSlotByLoginUser\\Validator',
],
In addition HTTP DELETE method will not validate using JSON row body from POSTMAN you have to pass query parameters and in your controller you need to get validated data using plugin like below:
$recruiterId = $this->getInputFilter()->getValues()['recruiterId'];
$timeSlotId = $this->getInputFilter()->getValues()['timeSlotId'];
If you want to delete a resource your should use the url that includes the route to that entity. This means the id would be in your route parameters, not in your query parameters. So the id is a route parameter/identifier and the RestController will search your entity using the identifier in the fetch($id) method of your resource listener. The listener should return a not found (404) response in case the entity with that identifier doesn't exist.
The content validation you mention in your question is for validating POST/GET parameters. So there is no need for such validator in case of a delete request.
So say for example you want to delete a Slot you would have a route:
api/v1/slots/[slot_id]
And if you want to delete Slot with id 1 you would send a delete request to:
DELETE
api/v1/slots/1
Your listener should simply return a 404 response in case a Slot with slot_id 1 doesn't exist.
I see you're using RPC Rather than Rest style - if you're passing the parameter using the query string you will have to validate it yourself inside the controller, for example:
public function someActionMethod()
{
$id = $this->getRequest()->getQuery('id');
$validator = new Input('id');
$validator->getValidatorChain()
->attach(new \Zend\Validator\NotEmpty())
;
$validator->getFilterChain()
->attach(new StringToUpper())
;
$inputFilter = new InputFilter();
$inputFilter
->add($validator)
->setData($this->getRequest()->getQuery())
;
if( ! $inputFilter->isValid()) {
return new \ZF\ApiProblem\ApiProblemResponse(
new ApiProblem(400, $inputFilter)
);
}
}
Apigility won't use any of the config generated using the UI to validate those fields for you wuen passed via query string as it says in the docs - they will be ignored. You would need to generate the valaidator yourself.
You could set it up to generate the validation using a config if you wished and then load the validator inside the controller to save writing boiler plate code as above.
I think the title pretty much says it all... Brand new to Deployd, so any pointers about how best to go about this are appreciated.
To get the objects in a collection created by the user (I assume you're using the javascript library dpd.js):
// Get the current user:
var currentUser;
dpd.users.me(function(result, error) {
currentUser = result;
});
// query your collection with your currentUser id as parameter
dpd.yourcollection.get({creator:currentUser.id}, function(result) {
// Do something with the result
console.log(result);
});
Your collection should have a property "creator" that contains the id of the user who created the object (*).
Then, to secure your backend, go to the dashboard, in the ON_GET tab of your collection and secure it with this code:
cancelUnless(isMe(this.creator), "You have to be the creator to view this item", 401);
More info about cancellUnless() and isMe() here:
http://docs.deployd.com/docs/collections/reference/event-api.md#s-cancelIf%28%29,%20cancelUnless%28%29-764
The good practice to secure your collections is to allow queries only if user is logged:
cancelUnless(me,"You have to be connected to view this item", 401);
Users collections should be particularly well secured (allow ON_PUT only by admin or something like that).
*: to automatically store the currentUserId in the creator property, you could also add this in the ON_POST event in the dashboard:
this.creator = me.id;
More info here: http://docs.deployd.com/docs/collections/reference/event-api.md#s-me-764
As of version 0.8.9, event ONBEFOREREQUEST exists and you could just put this code in there:
cancelUnless(me);
query.creator = me.id;
This means that for every request sent to that endpoint, creator key would be queried to have the currently logged in user's id. If there's not currently logged in user, the request is canceled.
I am building a RESTful API for a web site that allows users to create widgets and tabs containing widgets (think igoogle.com/ netvibes.com) and I want to share my URL design for your insights.
Here are the simple rules:
There is a static list of widgetTypes available for a user to pick.
A user can create one or more widgetInstances of each widgetType.
A user can create one or more tabs/ dashboards containing widgetInstances
This API needs to only serve JSON that will be consumed by JavaScript. We can also assume that all authentication will be taken care of through cookies.
The API needs to serve:
CRUD of user's Tabs
CRUD of specific user widgetInstances
Retrieval of all tabs for a user
Retrieval of all widgetInstances for a given tab.
Retrieval of all available widgetTypes for a user to add widgets from.
Design:
Tab controller:
widgetAPI.com/tabs -> Returns meta data (id, title) of all tabs available to a user.
widgetAPI.com/tabs/1 -> Returns meta data (title) of tab id 1. If sent with POST, updates tab id 1.
widgetAPI.com/tabs/1/widgets > Returns all widgetInstances of tab id 1.
Question 1: Ideally I'd like to follow the design of widgetAPI.com/tabs/1 also returning all the widgetInstances of the given tab but with that design, widgetAPI.com/tabs may return far too much data as I would have to return all the widgets for all the tabs. Hence I need to create a separate "widgetAPI.com/tabs/1/widgets" URL but that also has to return the tab meta data as I don't want to make two HTTP calls to get meta data & widgets. Please advise as I am not sure of the best approach here.
widgetAPI.com/tabs/create -> Create a new tab
widgetAPI.com/tabs/delete/123 -> Delete tabid 123
Widget Controller:
widgetAPI.com/widgets/123 -> Return data for widgetInstanceId 123. Updates 123 if sent through POST.
widgetAPI.com/widgets/Create?typeID = 2 -> Creates a new widgetInstance of typeid = 2. This will only be a POST request so typeId could be a post parameter.
widgetAPI.com/widgets/delete/123 -> Delete widgetInstance 123
Question 2 So there is one rule I havent been able to fulfill yet. I need to return all the widgetTypes available and I am not sure how to fit this request into the previous two controllers. I am currently leaning towards just serving this separately. So something like widgetAPI.com/getWidgetTypes. Thoughts?
Thanks guys. If you could critique on the overall design, just address the questions or mention anything I should watch out for, that would be great as this is my first time designing a RESTful app. Thanks again.
widgetAPI.com/tabs/1 -> Returns meta data (title) of tab id 1. If sent
with POST, updates tab id 1.
A POST to the above URL should not update tab 1. A PUT to that URL should update tab 1.
widgetAPI.com/tabs/create -> Create a new tab
To create a new tab, you should POST to widgetAPI.com/tabs
widgetAPI.com/tabs/delete/123 -> Delete tabid 123
To delete tab 123, send a DELETE to widgetAPI.com/tabs/123
widgetAPI.com/widgets/123 -> Return data for widgetInstanceId 123. Updates 123 if sent through POST
To update widget 123, send a PUT to widgetAPI.com/widgets/123
widgetAPI.com/widgets/Create?typeID = 2 -> Creates a new widgetInstance of typeid = 2. This will only be a POST request so typeId could be a post parameter.
To create a new widget, send a POST to widgetAPI.com/widgets. The typeId should be part of the POST request body.
widgetAPI.com/widgets/delete/123 -> Delete widgetInstance 123
To delete widget 123, send a DELETE request to widgetAPI.com/widgets/123
Answer 1: I like the widgetAPI.com/tabs/1/widgets URL design. Also, I like the idea of making 2 separate calls for the metadata and the data.
Answer 2: I think you should do this with a separate controller. I don't like the URL though. Instead, I like HTTP GET widgetAPI.com/widget-types or just widgetAPI.com/widgettypes.
As a general rule, unless you want your clients to be able to create URL identifiers, follow this pattern:
URL: /whatever-resource
GET - returns all resources of this type
POST - create a new resource
URL: /whatever-resource/{id}
GET - return single resource with that id
PUT - update resource with that id
DELETE - delete resource with that id
You can also allow PUT requests to /whatever-resource/{id} to create a resource, but the client / user must specify the id (implicitly, the URL, since the URL contains the id). If you don't want users to provide this, but rather have the server generate it, then POST to /whatever-resource to create the resource.
Hi I am using the AutoCompleteExtender ajax control. I am getting the list of strings in LIST collection. I want to populate only those strings, which user typing as prefix text. how to do this. I am following the example given in ajax toolkit.let say user typing "ca" then if list contain the list like,
'cat', 'dog', donkey', 'mouse','cart'....etc.
Then it should populate only 'cat' and cart'.
How to achieve this?
In the example there's a description of the properties. Quote:
ServiceMethod - The web service method to be called. The signature of
this method must match the following:
[System.Web.Services.WebMethod]
[System.Web.Script.Services.ScriptMethod]
public string[] GetCompletionList(string prefixText, int count) { ... }
Note that you can replace
"GetCompletionList" with a name of
your choice, but the return type and
parameter name and type must exactly
match, including case.
ServicePath - The path to the web service that the extender will
pull the word\sentence completions
from. If this is not provided, the
service method should be a page
method.
So you need write a web service which will contain a method returning the list of suggestions based on the user input.
Our web application needs one common parameter in every action method.
In our case it is the customer account id and we need to support the following scenarios
a. A group of users might have the same account id which can be derived from the user profile.
b. Customer Support team should be able explicitly supply the account id of a customer and also should be able to switch the account on any page
We are trying to not to use asp.net session to store this kind of data.
Are there any other options to store and manage this kind of common parameter data?
Write it out as an ecrypted value to hidden field on your master page and supply the value to every view. When the user is in a a customer role, place a change account "control" on the page that is able to retrieve and update the account data -- via AJAX, perhaps -- to change the current account id. It might be easiest to do this with a custom base controller which gets the data via the ValueProvider directly and stores it as a property on the page rather than having it be a parameter to every method.
Use Routing for the value. So if you need to change the id you can use another URL or post it as a parameter.
Whenever you need the value just ask the ValueProvider for it.
In case it is blank - use the one from user profile.
Of course you'd better write small method that will do just that:
// Register route like:
route.MapRoute("ProvidesAccountId", "{controller}/{id}/account{accountId}/{action}.aspx")
// Property on the base controller
protected Account CurrentAccount {
get {
var accountId = ValueProvider.GetValue<int?>("accountId"); // GetValue is just a helper
if (accountId.HasValue)
return YourRepositor.GetAccountBy(accountId.Value);
return CurrentUser.Account;
}
}
Not to use current user's account hit the URL: Profile/123/account/Edit.aspx
To use another account you can hit the URL: Profile/123/account-456/Edit.aspx
You get the idea.
Cheers,
Dmitriy.