I have recently used Java Spring to create REST services. In it, there were annotations for binding each specific function to a different REST query. Lets not go too technical here, as a psuedo, it was like this:
/** list records */
#bind("/list", WebMethod.GET)
List<Record> getRecords()
{
}
/** get record */
#bind("/record", WebMethod.GET)
Record getRecord()
{
}
/** add record */
#bind("/record", WebMethod.POST)
void addRecord()
{
}
Now I am given a tesk to perform a REST with Casablanca SDK in C++, but in every tutorial I checked covers a single GET or POST request. Is it possible to bind multpile requests to custom targets similar to Spring in Casablanca SDK?
You can make a single get function where you capture all get requests, then just get the path from the query and use a switch to call different functions to process the request for that path.
Related
I am developing an TYPO3 extbase extension to connect to my external application that has a REST-API. All I want is to retrieve the data from my REST-API and pass this data to the controller. Since I am quite new to extbase development, I didn't really find any resources about Repository interacting with a webservice. Only documentation about Repository that interacts with a database (MySQL, PostgreSQL, ..)
I would like to know, where should I place the cURL-Request Function to connect to the API? In the Model? In the Repository? How can the Controller in my Extension access that data? From Model or Repository?
What would be the best practice for retrieving the data from my external application/database ? (the data retrieved from the application is JSON-formatted)
thanks for any advice / help!
What we usually do is create a Service (in Classes/Service) and use that to connect to the webservice and fetch the data. If you want Models, you can create them there as well.
However, now that I think about it, technically it should be a Repository. It shouldn't matter where the Repository gets its data. Extbase shouldn't have any problem with a completely custom Repository (not extending any other class).
Lets take a simple list -> detail view and make it as simple as possible in order to get some results. Then you can add some helper classes and include them via namespaces etc.
ListAction (yourExtension/Classes/Controller/EventController)
/**
* action list
*
* #return void
*/
public function listAction()
{
$events = (some function which gets all the available events and results to a json output)
$decodedEvents = json_decode($events , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvents);
$this->view->assign('events', $decodedEvents);
}
List HTML (yourExtension/Resources/Private/Templates/Event/List)
<f:for each="{events}" as="event">
<h2>{event.title}</h2>
</for>
Now if i am not wrong, every event has a uid or something that identifies it. So on the same template you can do the following:
<f:for each="{events}" as="event">
<h2>{event.title}</h2>
<f:link.action action="detail" arguments="{eventId: event.uid}">More</f:link.action>
</f:for>
What this does, is to generate a link which links to your detail action with an extra parameter, the eventId.
Bedore TYPO3 renders the detail page it will go through your detail action to get the information for this specific event in order to display them.
DetailAction (yourExtension/Classes/Controller/EventController)
/**
* action detail
*
* #return void
*/
public function detailAction()
{
$args = $this-request->getArguments();
$eventId = $args['eventId'];
$getEvent = (some function that gets a specific id and results to a json output)
$decodedEvent = json_decode($getEvent , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvent );
$this->view->assign('event', $decodedEvent );
}
What this does, it to retrieve the argument that we specified in order to get the identifier of the event and include it on your api request to get the event back. Then we just just decode it to a normal array and we send the results to the FrontEnd.
Detail HTML (yourExtension/Resources/Private/Templates/Event/Detail)
<h2>{event.title}</h2>
Thats a simple list -> detail proccess on TYPO3. You will oft see that the detail view as show but is basically the same thing.
You can replace the variables with a dummy json values and play around. I tested the code while i was writing this answer and it works.
Classes/Service
As for the Classes/Service that mentioned by Rudy Gnodde, you can put your library in there (assuming that you already have coded some functions like getEvent, getAllEvents, getPersons etc.) and then call them in the controller.
use \Vendor\YourExtension\Service\RestApiClass;
/**
* action list
*
* #return void
*/
public function listAction()
{
$events = RestApiClass::getAllEvents();
$decodedEvents = json_decode($events , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvents);
$this->view->assign('events', $decodedEvents);
}
/**
* action detail
*
* #return void
*/
public function detailAction()
{
$args = $this-request->getArguments();
$eventId = $args['eventId'];
$getEvent = RestApiClass::getEvents($eventId);
$decodedEvent = json_decode($getEvent , true);
(in case you need to see what you got back)
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($decodedEvent );
$this->view->assign('event', $decodedEvent );
}
If you need more information feel free to ask.
Best regards
We are building some plugins in Microsoft Dynamics CRM by inheriting from IPlugin. We have these configured so they fire whenever an Account is updated.
The problem is the plugins are calling our services, which causes our service to respond with an update. We are doing some pretty hacky things right now to prevent these cyclical updates from happening.
We were wondering if there was a way to pass a value to the IOrganizationService service (the web service) that a plugin can look at. Our other system could send a flag ("hey, don't bothing sending an update!") and the plugin could skip calling back.
Can we pass parameters from web service to the plugins?
Good idea could be usage of custom flag-field. For example you add bit field and call it CallFromExternalSystem. So when you make an update from your external system through IOranizationService you just fill this flag with true field and in plugin you can check condition that this field is present in fields list so you have no need to call external system endpoint again.
We decided the correct solution was to use the value found in IPluginExecutionContext.InputParameters["Target"]. In the case of an Update, this returns an Entity containing attributes for all the attributes that were updated.
We basically have a list of attribute names we cared about. We loop through names and see if any of them appear in the entity attribute list. If so, we send an update to our other system. The good news is, Dynamics CRM ignores updates where the values don't actually change, so trying to update a value to itself is no-op.
public void Execute(IServiceProvider serviceProvider)
{
IPluginExecutionContext context = serviceProvider.GetService(typeof(IPluginExecutionContext));
Entity entity = (Entity)context.InputParameters["Target"];
string[] fields = new string[] { "name", "statecode", "address1_line1" };
bool hasUpdates = fields.Where(f => entity.Attributes.Contains(f)).Any();
if (!hasUpdates)
{
return;
}
}
i need to create a webservices with symfony2 ive read the official article http://symfony.com/doc/current/cookbook/web_services/php_soap_extension.html
in the example it creates a instance of SoapServer with a parameter routing a .wsdl file, what is this file? i didnt found too much documentation about soap in symfony. some help with this please?
public function indexAction()
{
$server = new \SoapServer('/path/to/hello.wsdl');
$server->setObject($this->get('hello_service'));
$response = new Response();
$response->headers->set('Content-Type', 'text/xml; charset=ISO-8859-1');
ob_start();
$server->handle();
$response->setContent(ob_get_clean());
return $response;
}
I'm not sure if you've found your answer or not. Just for anybody else that might run into such a problem:
WSDL is the language in which the Web Services are defined and described. It's basically a XML file containing the input/output parameters of each and every function that are served by a server. it also contains some information about the server itself, that's providing the services.
in order to be able to create a webservice, you need to use the code you've provided, which would in fact prepare Symfony to serve the clients on "/path/to/hello.wsdl" (in my example this path is /YourDesiredPathFor/services.wsdl), and also, you need to provide a valid WSDL document that contains the information mentioned above in correct WSDL format. the problem is that Symfony (or even PHP itself for this matter) has no means of creating the file automatically.
to solve the matter, you need to use an external WSDL generator. I'd suggest using PHP-WSDL-Creator. It uses annotations written inside php files to create the WSDL file and also runs the SoapServer. that means that you won't even need the code that you've provided. it also has proper interfaces and addons that provide you with clients for different protocols and languages.
you need to tweak it a little bit though! if you want it to be by symfony standards, i think you would need to rewrite some parts of it; but if you'd like to use it as an external library, it could also work!
the way i did it was by copying the extracted files into ./vendor/php_wsdl/lib/php_wsdl/src (long it is, isn't it? maybe an easier path would also work!); then defined a php_wsdl.php in ./vender/php_wsdl/lib/php_wsdl:
<?php
require_once __DIR__. '/src/class.phpwsdl.php';
class WSDL_PhpWsdl extends PhpWsdl{
}
next, in the "./app/autoload.php", i added the following line to enable Symfony to use the created extension:
require_once __DIR__. '/../vendor/php_wsdl/lib/php_wsdl/php_wsdl.php';
just one thing! the extension needs a "cache" folder in order to cache the created wsdl files and all. unfortunately because i need to finish the project quickly, i don't have enough time to manage the cache folder as it should be. there are definitely better ways than my way, and i would really be glad to know about them.
anyway, now you need to use the extension's capabilities! to do so, i created a "ServerController" for the bundle i was using:
<?php
namespace Tara\PageControllerBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
class ServiceController extends Controller
{
function wsdlAction(){
\PhpWsdlServers::$EnableRest = false;
$soap= \WSDL_PhpWsdl::CreateInstance(
null, // PhpWsdl will determine a good namespace
$this->generateUrl('service_wsdl', array(), true), // Change this to your SOAP endpoint URI (or keep it NULL and PhpWsdl will determine it)
__DIR__. '/cache', // Change this to a folder with write access
Array( // All files with WSDL definitions in comments
dirname(__FILE__). '/../Services/MyService.php'
),
null, // The name of the class that serves the webservice will be determined by PhpWsdl
null, // This demo contains all method definitions in comments
null, // This demo contains all complex types in comments
false, // Don't send WSDL right now
false // Don't start the SOAP server right now
);
if($soap->IsWsdlRequested()) // WSDL requested by the client?
$soap->Optimize=false; // Don't optimize WSDL to send it human readable to the browser
$soap->RunServer();
}
}
as you can see, the path to the cache folder is on a local directory, which means it has to be created manually and in ./src/Tara/PageControllerBundle/Controller (obviously in my case; you will need to change the paths). I'm sure there are better ways to manage the cache folder.
there is a line there:
dirname(__FILE__). '/../Services/MyService.php
this line tells the extension where to look for annotations in order to create the WSDL page. you also need to define a route to "service_wsdl":
service_wsdl:
pattern: /YourDesiredPathFor/services.wsdl
defaults: {_controller: TaraPageControllerBundle:Service:wsdl}
as you can see, the controller is ServiceController and the function responsible for it is wsdlAction; the exact function that was defined!
just as an example, i would provide my own MyService.php:
<?php
namespace Tara\PageControllerBundle\Services;
use Tara\PageControllerBundle\Model\...
/**
* #service Tara\PageControllerBundle\Services\MyService
*/
class MyService
{
/**
* Function Create
*
* #param string $link
* #param string $title
* #param string $image
* #param string $description
* #return boolean Status of the creation
* #pw_rest POST /YourDesiredPathForAction/create Create The Object
*
*/
public function Create($link, $title, $image, $description)
{
// your code that handles the data goes here. this is not a part of the annotations!
return (bool)$result;
}
}
now, you might be able to use a SoapClient to connect to your web service at
http: //your-server.something/YourDesiredPathFor/services.wsdl?wsdl
and call the Create function! you can also check the output of the extension by opening the above written address. the extension also provides a "human-readable" version at
http: //your-server.something/YourDesiredPathFor/services.wsdl.
i would be glad to know if this was any help to anyone! :)
SOAP is a more general concept that Symfony assumes you are familiar with. There is an example WSDL at the bottom of the page that you link to. Take a look at tutorials about SOAP and WSDL and then try to recreate what they're doing in the Symfony page.
SOAP Tutorial
WSDL Tutorial
Goal:
Retrieve data from Dynamics CRM 2011 to my database from SQL server R2 by using webservice through integration services (SSIS). Webservice needed to be located inside of SSIS. Gonna use the data for data warehouse.
Problem:
How do I do it?
We only write to Dynamics so I can't address the specific method name but the general idea below should get you started.
Assumptions
Two variables have been defined in your package and they are passed to the script component as ReadOnlyVariables: CrmOrganizationName, CrmWebServiceUrl.
A script component has been added to the dataflow as a Source component. On the Inputs and Outputs tab, an appropriate number of columns have been added to Output 0 (or whatever you define your output collection as) with appropriate data types.
Inside the script, add a web reference to your CRM instance. This code assumes it's called CrmSdk.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Windows.Forms;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
// web reference
using CrmSdk;
[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
public override void CreateNewOutputRows()
{
// Get a reference to the CRM SDK
CrmSdk.CrmService CrmService = new CrmSdk.CrmService();
// An Authentication Token is required because CRM requires an OrganizationName
// to identify the Organization to be used
CrmSdk.CrmAuthenticationToken token = new CrmSdk.CrmAuthenticationToken();
token.AuthenticationType = 0;
token.OrganizationName = this.Variables.CrmOrganizationName;
CrmService.CrmAuthenticationTokenValue = token;
// Use default credentials
CrmService.Credentials = System.Net.CredentialCache.DefaultCredentials;
// Get the web service url from the config file
CrmService.Url = this.Variables.CrmWebServiceUrl;
//////////////////////////////////////////////////
// This code is approximate
// Use the appropriate service call to get retrieve
// data and then enumerate through it. For each
// row encountered, call the AddRow() method for
// your buffer and then populate fields. Be wary
// of NULLs
//////////////////////////////////////////////////
foreach (CrmSdk.entity person in CrmService.Get())
{
Output0Buffer.AddRow();
Output0Buffer.FirstName = person.FirstName;
Output0Buffer.LastName = person.LastName;
}
}
}
Caveats
There is no error handling, checks for nulls or anything elegant. The service should probably have been defined with the using statement, etc, etc, etc. It should provide an appropriate starting point for understanding how to consume a web service and load data into the pipeline.
The easiest solution for your requirement is to use a third-party library for SSIS. The commercial COZYROC SSIS+ library includes Dynamics CRM adapters, which support all deployment models: Premise, Live, Hosted, Federation, Office 365.
The goal is to create a rest-full web service using JAX-RS that will selectively return the result either in json or xml format, depending on the user request. For example, if the user issues a GET request in the following fashion the results will be returned in json format:
http://api.myurl.com/order/1234.json
Similarly, if the user issues a get in the following request, the results will be returned in xml format:
http://api.myurl.com/order/123.xml
I don't want to use request parameters to do this (i.e. http://api.myurl.com/order/123?format=json). Using the .json or .xml post-fix seems more intuitive to me.
What would be the best strategy for doing this using the JAX-RS api?
One way would be to use #Path annotations more thoroughly:
#GET
#Path("/order/{id}.xml")
#Produces("application/xml")
public Order getOrderAsXML(#PathParam("id") int id) {
return realGetOrder(id);
}
#GET
#Path("/order/{id}.json")
#Produces("application/json")
public Order getOrderAsJSON(#PathParam("id") int id) {
return realGetOrder(id);
}
private Order realGetOrder(int id) {
// ...
}
However I'd be inclined to have a single method serving up both and let the client and supporting JAX-RS framework use content negotiation to decide the serialization method.