Closed matthttam closed 2 years ago
2020-02-22 18:17:41 Error: [Cake\Datasource\Exception\MissingDatasourceConfigException] The datasource configuration "app" was not found. in /home/bil/projects/freshserviceinventory/vendor/cakephp/cakephp/src/Datasource/ConnectionManager.php on line 203 Exception Attributes: array ( 'name' => 'app', ) Stack Trace:
Request URL: /Tickets Client IP: 127.0.0.1
So my implementation is very basic at the moment.
I've got a TicketsController with an index function that only does this: $this->loadModel('Tickets', 'Endpoint');
I've created a src/Webservice/Driver/Freshservice.php driver which extends AbstractDriver and calls setClient in its initialize function.
I've created a Model/Endpoint/TicketsEndpoint that extends Endpoint and has nothing else in it. I've created a src/Webservice/TicketsWebservice that extends Muffin\Webservice\Webservice\Webservice . This has a function _executeReadQuery like so:
`protected function _executeReadQuery(Query $query, array $options = []) {
$response = $this->getDriver()->getClient()->get('/tickets',[],[ 'auth' => ['username' => '', 'password' => 'X']]);
//debug('here');
if (!$response->isOk()) {
return false;
}
//debug($response->getJson());
$resources = $this->_transformResults($query->getEndpoint(), $response->getJson()['tickets']);
//debug($resources);
//return '';
return new ResultSet($resources, count($resources));
}`
So for some reason my setup is trying to look in config/app.php for datasource -> app. If I add this to my datasources:
'app' => [ 'className' => 'Muffin\Webservice\Datasource\Connection', 'service' => 'App\Webservice\Driver\Freshservice', 'host' => 'mysite.freshservice.com/api/v2/', //'api_key' => '', ],
It actually works. But I really don't want "app" to be the name of the datasource and I don't see a way to change the configuration.
debug_kit shows that Muffin\Webservice\Model\EndpointLocator->get is being passed:
'Tickets' [ 'alias' => 'Tickets', 'className' => 'App\Model\Endpoint\TicketsEndpoint' ]
Cake\Datasource\ConnectionManager::get is being passed 'app'
As I mentioned earlier, I tracked down this 'app' argument to the defaultConnectionName(): string which is taking my class name 'App\Model\Endpoint\TicketsEndpoint', splitting by '\', reversing it, then array_split with 3, 2... which is just getting the word 'app'.
` public static function defaultConnectionName(): string { $namespaceParts = explode('\', static::class); $plugin = array_slice(array_reverse($namespaceParts), 3, 2);
return Inflector::underscore(current($plugin));
}`
I'm probably just doing something wrong or I need to add some sort of config somewhere but I can't figure out how to fix it.
Have you made App\WebService\FreshWebservice
class? Also your driver class should be App\WebService\Driver\Fresh
, proper casing and naming convention matters. Then you need datasource config named fresh
in your app config.
Refer to the Github webservice for example https://github.com/cvo-technologies/cakephp-github
So the application api I am pulling from is called "Freshservice". Thanks for the help on understanding the need for a FreshserviceWebservice that is then extended by things like the TicketsWebservice. However, this didn't correct the issue.
The plugin is still trying to find a datasource called 'app'
Just to be clear, I am using the 3.0.0 beta of your plugin as I am in CakePHP 4. So I replicated the example GitHub webservice and updated all of the functions and namespaces. Still, the app is trying to find a datasource called 'app' instead of 'Freshservice'.
Here is my setup: Freshservice Driver located in src\Webservice\Driver\Freshservice.php: `namespace App\Webservice\Driver;
use Cake\Http\Client; use Muffin\Webservice\Webservice\Driver\AbstractDriver;
class Freshservice extends AbstractDriver {...`
Freshservice Webservice located in src\Webservice\FreshserviceWebservice.php: `namespace App\Webservice;
use Muffin\Webservice\Datasource\Query; use Muffin\Webservice\Datasource\ResultSet; use Muffin\Webservice\Webservice\Webservice;
class FreshserviceWebservice extends Webservice {`
Tickets Webservice located in src\Webservice\TicketsWebservice.php `namespace App\Webservice;
use Muffin\Webservice\Datasource\Query; use Muffin\Webservice\Datasource\ResultSet; use Muffin\Webservice\Webservice\Webservice;
class TicketsWebservice extends FreshserviceWebservice { public function initialize(): void { parent::initialize(); }`
Tickets Endpoint located in src\Model\Endpoint\TicketsEndpoint.php `namespace App\Model\Endpoint;
use Muffin\Webservice\Model\Endpoint;
class TicketsEndpoint extends Endpoint {`
So, with these files in place, shouldn't it be looking for a datasource called 'freshservice'? Instead I'm getting the error: 'The datasource configuration app was not found in config/app.php.'
I thought I'd take another look and try to figure this out. cake-4.x branch file: src/Model/Endpoint.php Method defaultConnectionName() which is used when loaded with the EndpointLocator I think.
public static function defaultConnectionName(): string
{
$namespaceParts = explode('\\', static::class);
$plugin = array_slice(array_reverse($namespaceParts), 3, 2);
return Inflector::underscore(current($plugin));
}
One endpoint I am using has the following static::class "App\Model\Endpoint\AssetsEndpoint" The function above sets namespace parts by exploding that class name as such: $namespaceParts = Array ( [0] => App [1] => Model [2] => Endpoint [3] => AssetsEndpoint ) Then we do array_reverse($namespaceParts); Which returns: Array ( [0] => AssetsEndpoint [1] => Endpoint [2] => Model [3] => App ) This is passed into array_slice(thearray, 3, 2) This returns Array ( [0] => App )
So, this function makes sense if I were to create a plugin... but I'm using it directly in my project to perform API calls. Is that not intended?
So I spent today learning how to put together a plugin... getting that working... then learning how to split it into its own repository... then submitting it to packagelist. I now have a working freshservice API plugin requiring your plugin. It is now looking for the proper config name 'freshservice' instead of 'app'.
https://bitbucket.org/matt_henry_ops/freshservice
It is still very much a work in progress but this is working so far! Sorry for any confusion on my part. I thought I could just use the webservice plugin directly in my regular application... If I should be able to do this then the above code should either account for this or documentation on how to set the proper config name would be good.
Thanks!
Thanks for providing the detailed analysis and congrats on learning all the new stuff related to plugin development and publishing, that will definitely help you in future :slightly_smiling_face:.
So when loading an endpoint class we don't have any info about which webservice it's related to and what connection it should use. Hence we have to resort to guess work based on the FQCN to get the connection name.
We could try to improve the connection name guessing or just clarify that users should override the Endpoint::defaultConnectionName()
and make it return the connection to use. Since we don't really have much documentation the endpoint class under Create an endpoint (optional)
section in readme could include the defaultConnectionName()
method.
Given all the various classes needed for a webservice I would recommend organizing them using a plugin regardless. One can make an "app plugin" and doesn't necessarily need to publish it to packagist.
Closed by #88
A driver with a class of 'App\Webservice\Driver\FreshService' ends up trying to load a datasource named 'app' instead of something like Webservice\FreshService pr Driver\FreshService. I've tracked down the issue to Webservice/src/Model/Endpoint.php function defaultConnectionName(): string
I'm new to this framework however the default behavior of looking for Datasource > app didn't seem quite right.