I'm running API tests with codeception using Yii2. Inside tests folder I have another folder codeception in which I have all the configuration files, and this is where I run my tests.
This is my codeception.yml file
paths:
tests: tests
output: tests/_output
data: tests/_data
support: tests/_support
envs: tests/_envs
actor_suffix: Tester
extensions:
enabled:
- Codeception\Extension\RunFailed
And this is my api.suite.yml:
class_name: ApiTester
modules:
enabled:
- \Helper\Api
- \Helper\FactoryHelper
- REST:
depends: PhpBrowser
url: 'http://apitest.sb.dev/api/'
- Db:
dsn: 'mysql:host=127.0.0.1;dbname=test'
user: 'root'
password: ''
dump: 'codeception/_data/dump.sql'
populate: true
cleanup: true
reconnect: true
- Yii2:
configFile: ../config/tests/unit.php
part: [orm, email]
cleanup: false
- DataFactory:
factories: codeception/_support/factories
depends: Yii2
A typical test I have would have functions from the generated file ApiTesterActions. My problem is that whenever I run a test Ill get an error indicating that ApiTesterActions file is completely not seen. An example:
Codeception: [RuntimeException] Call to undefined method
ApiTester::sendDELETE
removing sendDelete from my test and using any other method that is in that file will also give an error.
Example for test:
public function index(ApiTester $I) {
$I->wantTo('view list via api');
$I->sendGET($this->_getUrl(), ['client' => 10]);
$I->seeResponseContainsJson(["data" => $this->menus]);
$I->seeResponseCodeIs(200);
$I->wantTo("try to view menus with client which doesn't exist");
$I->sendGET($this->_getUrl(), ['client' => 150]);
$I->seeResponseCodeIs(403);
$I->seeResponseIsJson();
$I->seeResponseContains(Yii::t('app', 'You need permission'));
}
What am I doing wrong??!
Edit: This is the ApiTester.php
/**
* Inherited Methods
* #method void wantToTest($text)
* #method void wantTo($text)
* #method void execute($callable)
* #method void expectTo($prediction)
* #method void expect($prediction)
* #method void amGoingTo($argumentation)
* #method void am($role)
* #method void lookForwardTo($achieveValue)
* #method void comment($description)
* #method \Codeception\Lib\Friend haveFriend($name, $actorClass = NULL)
*
* #SuppressWarnings(PHPMD)
*/
class ApiTester extends \Codeception\Actor
{
use _generated\ApiTesterActions;
/**
* #param string $name
* #return mixed
*/
public function loadFixture($name)
{
return include 'codeception/fixtures/' . $name . '.php';
}
/**
*/
public function autoAuthenticate($key = '')
{
if ($key == '') {
$key = '17sV24Ku83q-BxeNbXHUWNcBO5iVQxDK';
}
return $this->amBearerAuthenticated($key);
}
}
Related
I have running a simple symfony/skeleton.
Basically I followed the documentation on "json_login", however Symfony does not authenticate the user when the login route is called. I test via Thunder Client in VSC.
I can call the /login route method, but the user object is basically NULL. In the meantime I have also tried to use my own authenticator, however this has not brought me any further in all variations. I have first tested with Symfony 6.1., last on version 6.2..
I would be very grateful for a tip or a link to a working tutorial. Thanks
I have already created a user entity via make:user which has the following structure:
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 180, unique: true)]
private ?string $email = null;
#[ORM\Column]
private array $roles = [];
/**
* #var string The hashed password
*/
#[ORM\Column]
private ?string $password = null;
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUserIdentifier(): string
{
return (string) $this->email;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
Then I created a controller for the /login and /logout routes with the following structure:
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\User;
use Symfony\Component\Security\Http\Attribute\CurrentUser;
class UserLoginController extends AbstractController
{
#[Route('/login', name: 'app_login')]
public function index(#[CurrentUser] ?User $user): Response
{
if (null === $user) {
return $this->json([
'message' => 'missing credentials',
], Response::HTTP_UNAUTHORIZED);
}
return $this->json([
'message' => 'Welcome to your new controller!',
'path' => 'src/Controller/ApiLoginController.php',
'user' => $user->getUserIdentifier()
]);
}
}
And my configuration for it looks like this:
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: "auto"
providers:
app_user_provider:
entity:
class: App\Entity\User
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
json_login:
check_path: /app_login
username_path: email
password_path: password
lazy: true
provider: app_user_provider
#custom_authenticator: App\Security\UserAuthenticator
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
I am new with PHP and Laravel.
I am using Laravel Framework version "laravel/framework": "5.4.*", and PHP 7.
I am building multilevel authentication.
I have been following https://www.youtube.com/watch?v=P8T3MjZPDdI
Alexander Curtis videos.
Error which I am getting is:
PHP Parse error: syntax error, unexpected '$login' (T_VARIABLE) in app/Exceptions/Handler.php on line 68
$login = 'admin.login';
[Mon May 8 20:53:30 2017] PHP Fatal error: Exception thrown without a stack frame in Unknown on line 0
Code is double check many times, from https://github.com/DevMarketer/multiauth_tutorial/archive/part_3.zip
I am using Laravel way to make auth, not any external package.
website /admin/login is showing blank page.
also webserver is php artisan serve.
I am stuck with this error.
<?php
namespace App\Exceptions;
use Exception; use Illuminate\Auth\AuthenticationException; use
Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
class Handler extends ExceptionHandler {
/**
* A list of the exception types that should not be reported.
*
* #var array
*/
protected $dontReport = [
\Illuminate\Auth\AuthenticationException::class,
\Illuminate\Auth\Access\AuthorizationException::class,
\Symfony\Component\HttpKernel\Exception\HttpException::class,
\Illuminate\Database\Eloquent\ModelNotFoundException::class,
\Illuminate\Session\TokenMismatchException::class,
\Illuminate\Validation\ValidationException::class,
];
/**
* Report or log an exception.
*
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
*
* #param \Exception $exception
* #return void
*/
public function report(Exception $exception)
{
parent::report($exception);
}
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
return parent::render($request, $exception);
}
/**
* Convert an authentication exception into an unauthenticated
* response.
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Auth\AuthenticationException $exception
* #return \Illuminate\Http\Response
*/
Second part of code where added multilogin part
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
$guard = array_get($exception->guards(),0);
switch ($guard) {
case 'admin':
# code...
$login = 'admin.login';
break;
default:
# code...
$login ='login';
break;
}
return redirect()->guest(route($login));
}
}
You forgot to remove the default sublime text suggestions:
So remove the "# code ... " before your $login declaration.
switch ($guard) {
case 'admin':
*# code...*
$login = 'admin.login';
break;
default:
*# code...*
$login ='login';
break;
}
I'm new with the symfony framework. I'm trying to create webservices with FOSRest bundle but I had a problems when I tried to implement the POST method for one entity with json.
Test:
public function testJsonPostNewTesteAction(){
$this->client->request(
'POST',
'/api/teste/new',
array(),
array(),
array(
'Content-Type' => 'application/json',
'Accept' => 'application/json'
),
'{"Teste":{"title":"O teu title"}}'
);
$response = $this->client->getResponse();
$this->assertJsonResponse($response, Codes::HTTP_CREATED);
}
Controller:
/**
*
* #Config\Route(
* "/teste/new",
* name="postTestNew"
* )
* #Config\Method({"POST"})
*
* #param Request $request the request object
*
* #return View|Response
*/
public function postNewTeste(Request $request){
return $this->processFormTest($request);
}
/**
* Method for create the process form to Advertisements
*
* #param Request $request
*
* #return mixed
*/
private function processFormTest(Request $request){
$form = $this->createForm(
new TesteType(),
new Teste()
);
$form->bind($request);
//$from->handleRequest($request);
if ($form->isValid()) {
$test = $form->getData();
return $test;
}
return View::create($form, Codes::HTTP_BAD_REQUEST);
}
The problem is when I use the handleRequest(), the method isValid() returns false because the form didn't submit. So I try to change the handleRequest to the bind method. In the last case, the method isValid() returns true but the method getData() returns a null object.
I don't know if the problem is the type form class bellow.
Type Form:
/**
* The constant name to that type
*/
const TYPE_NAME = "Teste";
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options){
parent::buildForm($builder, $options);
$builder->add("title", ValidationType::TEXT);
}
/**
* {#inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver){
$resolver->setDefaults(
array(
'csrf_protection' => false,
'cascade_validation' => true,
'data_class' => 'oTeuGato\DatabaseBundle\Entity\Teste'
)
);
}
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
public function getName(){
return AdvertisementType::TYPE_NAME;
}
I need to POST the entity with both ways. Anyone sugest anything for my problem?
Thanks for the patience!
Got the same issue with the form binding, the only way i found to solve it was to set an empty string to the form getName function:
/**
* Returns the name of this type.
*
* #return string The name of this type
*/
public function getName(){
return '';
}
And i would suggest to use the handleRequest method when you bind your data because the bind method is deprecated:
private function processFormTest(Request $request){
$form = $this->createForm(
new TesteType(),
new Teste()
);
$from->handleRequest($request);
if ($form->isValid()) {
$test = $form->getData();
return $test;
}
return View::create($form, Codes::HTTP_BAD_REQUEST);
}
It looks more like a hack but seems like it's the only way for now:
https://github.com/FriendsOfSymfony/FOSRestBundle/issues/585
I am not entirely sure but I think it's because you're sending the data with: 'Content-Type' => 'application/json'
in stead of 'Content-Type' => 'application/x-www-form-urlencoded'
Or at least I think that's the reason why the handleRequest is not doing it's thing.
I am planning to make a reverse geocoding based on the BazingaGeocoderBundle. A simple way to do that is write this simple code in the controller:
$result = $this->container
->get('bazinga_geocoder.geocoder')
->using('google_maps')
->reverse(48.79084170157100,2.42479377175290);
return $this->render("MinnAdsBundle:Motors:test.html.twig",
array('result'=>var_dump($result)));
Until here, things are going well.
My objective is to make the code nicer & resuable. So, I used this article to write my own GeocoderEventSubscriber as describer below:
<?php
namespace Minn\AdsBundle\Doctrine\Event;
use Doctrine\Common\EventSubscriber;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use Doctrine\ORM\Event\LifecycleEventArgs;
//use Geocoder\Provider\ProviderInterface;
use Bazinga\Bundle\GeocoderBundle\Geocoder\LoggableGeocoder;
/**
* Subscribes to Doctrine prePersist and preUpdate to update an
* the address components of a MotorsAds entity
*
* #author majallouli
*/
class MotorsAdsGeocoderEventSubscriber implements EventSubscriber {
protected $geocoder;
public function __construct(LoggableGeocoder $geocoder){
$this->geocoder = $geocoder;
}
/**
* Specifies the list of events to listen
*
* #return array
*/
public function getSubscribedEvents(){
return array(
'prePersist',
'preUpdate',
);
}
/**
* Sets a new MotorsAds's address components if not present
*
* #param LifecycleEventArgs $eventArgs
*/
public function prePersist(LifecycleEventArgs $eventArgs){
$motorsAds = $eventArgs->getEntity();
if($motorsAds instanceof \Minn\AdsBundle\Entity\MotorsAds){
if( !$motorsAds->getCountry()){
$em = $eventArgs->getEntityManager();
$this->geocodeMotorsAds($motorsAds,$em);
}
}
}
/**
* Sets an updating MotorsAds's address components if not present
* or any part of address updated
*
* #param PreUpdateEventArgs $eventArgs
*/
public function preUpdate(PreUpdateEventArgs $eventArgs){
$motorsAds = $eventArgs->getEntity();
if($motorsAds instanceof \Minn\AdsBundle\Entity\MotorsAds){
if( !$motorsAds->getCountry() ){
$em = $eventArgs->getEntityManager();
$this->geocodeMotorsAds($motorsAds,$em);
$uow = $em->getUnitOfWork();
$meta = $em->getClassMetadata(get_class($motorsAds));
$uow->recomputeSingleEntityChangeSet($meta, $motorsAds);
}
}
}
/**
* Geocode and set the MotorsAds's address components
*
* #param type $motorsAds
*/
private function geocodeMotorsAds($motorsAds,$em){
$result = $this->geocode
->using('google_maps')
->reverse($motorsAds->getLat(),$motorsAds->getLng());
$motorsAds->setCountry(
$em->getRepository("MinnAdsBundle:Country")->findCountryCode($result['countryCode']));
}
}
After that, I declared my EventSubscriber as a service:
services:
# ...
geocoder_motorsads.listener:
class: Minn\AdsBundle\Doctrine\Event\MotorsAdsGeocoderEventSubscriber
arguments: [#bazinga_geocoder.geocoder] # almost sure that the error is here!!
tags:
- { name: doctrine.event_subscriber }
Actually, I get this error:
ContextErrorException: Notice: Undefined property: Minn\AdsBundle\Doctrine\Event\MotorsAdsGeocoderEventSubscriber::$geocode in /home/amine/NetBeansProjects/tuto/src/Minn/AdsBundle/Doctrine/Event/MotorsAdsGeocoderEventSubscriber.php line 78
I am almost sure that error is in the declaration of arguments of the EventSubscriber. Is it #bazinga_geocoder.geocoder?
Thank you for your help!
Your property is $this->geocoder but you're calling $this->geocode, you're spelling it wrong.
With the introduction of Zend_Rest_Route in Zend Framework 1.9 (and its update in 1.9.2) we now have a standardized RESTful solution for routing requests. As of August 2009 there are no examples of its usage, only the basic documentation found in the reference guide.
While it is perhaps far more simple than I assume, I was hoping those more competent than I might provide some examples illustrating the use of the Zend_Rest_Controller in a scenario where:
Some controllers (such as indexController.php) operate normally
Others operate as rest-based services (returning json)
It appears the JSON Action Helper now fully automates and optimizes the json response to a request, making its use along with Zend_Rest_Route an ideal combination.
Appears it was rather simple. I've put together a Restful Controller template using the Zend_Rest_Controller Abstract. Simply replace the no_results return values with a native php object containing the data you want returned. Comments welcome.
<?php
/**
* Restful Controller
*
* #copyright Copyright (c) 2009 ? (http://www.?.com)
*/
class RestfulController extends Zend_Rest_Controller
{
public function init()
{
$config = Zend_Registry::get('config');
$this->db = Zend_Db::factory($config->resources->db);
$this->no_results = array('status' => 'NO_RESULTS');
}
/**
* List
*
* The index action handles index/list requests; it responds with a
* list of the requested resources.
*
* #return json
*/
public function indexAction()
{
// do some processing...
// Send the JSON response:
$this->_helper->json($this->no_results);
}
// 1.9.2 fix
public function listAction() { return $this->_forward('index'); }
/**
* View
*
* The get action handles GET requests and receives an 'id' parameter; it
* responds with the server resource state of the resource identified
* by the 'id' value.
*
* #param integer $id
* #return json
*/
public function getAction()
{
$id = $this->_getParam('id', 0);
// do some processing...
// Send the JSON response:
$this->_helper->json($this->no_results);
}
/**
* Create
*
* The post action handles POST requests; it accepts and digests a
* POSTed resource representation and persists the resource state.
*
* #param integer $id
* #return json
*/
public function postAction()
{
$id = $this->_getParam('id', 0);
$my = $this->_getAllParams();
// do some processing...
// Send the JSON response:
$this->_helper->json($this->no_results);
}
/**
* Update
*
* The put action handles PUT requests and receives an 'id' parameter; it
* updates the server resource state of the resource identified by
* the 'id' value.
*
* #param integer $id
* #return json
*/
public function putAction()
{
$id = $this->_getParam('id', 0);
$my = $this->_getAllParams();
// do some processing...
// Send the JSON response:
$this->_helper->json($this->no_results);
}
/**
* Delete
*
* The delete action handles DELETE requests and receives an 'id'
* parameter; it updates the server resource state of the resource
* identified by the 'id' value.
*
* #param integer $id
* #return json
*/
public function deleteAction()
{
$id = $this->_getParam('id', 0);
// do some processing...
// Send the JSON response:
$this->_helper->json($this->no_results);
}
}
great post, but I would have thought the Zend_Rest_Controller would route the request to the right action with respect to the HTTP method used. It'd be neat if a POST request to http://<app URL>/Restful would automatically _forward to postAction for example.
I'll go ahead and provide another strategy below, but maybe I'm missing the point behind Zend_Rest_Controller ... please comment.
My strategy:
class RestfulController extends Zend_Rest_Controller
{
public function init()
{
$this->_helper->viewRenderer->setNoRender();
$this->_helper->layout->disableLayout();
}
public function indexAction()
{
if($this->getRequest()->getMethod() === 'POST')
{return $this->_forward('post');}
if($this->getRequest()->getMethod() === 'GET')
{return $this->_forward('get');}
if($this->getRequest()->getMethod() === 'PUT')
{return $this->_forward('put');}
if($this->getRequest()->getMethod() === 'DELETE')
{return $this->_forward('delete');}
$this->_helper->json($listMyCustomObjects);
}
// 1.9.2 fix
public function listAction() { return $this->_forward('index'); }
[the rest of the code with action functions]