1623719963
CloudNativeTV Fields Tested - Kubernetes
#kubernetes
1602964260
Last year, we provided a list of Kubernetes tools that proved so popular we have decided to curate another list of some useful additions for working with the platform—among which are many tools that we personally use here at Caylent. Check out the original tools list here in case you missed it.
According to a recent survey done by Stackrox, the dominance Kubernetes enjoys in the market continues to be reinforced, with 86% of respondents using it for container orchestration.
(State of Kubernetes and Container Security, 2020)
And as you can see below, more and more companies are jumping into containerization for their apps. If you’re among them, here are some tools to aid you going forward as Kubernetes continues its rapid growth.
(State of Kubernetes and Container Security, 2020)
#blog #tools #amazon elastic kubernetes service #application security #aws kms #botkube #caylent #cli #container monitoring #container orchestration tools #container security #containers #continuous delivery #continuous deployment #continuous integration #contour #developers #development #developments #draft #eksctl #firewall #gcp #github #harbor #helm #helm charts #helm-2to3 #helm-aws-secret-plugin #helm-docs #helm-operator-get-started #helm-secrets #iam #json #k-rail #k3s #k3sup #k8s #keel.sh #keycloak #kiali #kiam #klum #knative #krew #ksniff #kube #kube-prod-runtime #kube-ps1 #kube-scan #kube-state-metrics #kube2iam #kubeapps #kubebuilder #kubeconfig #kubectl #kubectl-aws-secrets #kubefwd #kubernetes #kubernetes command line tool #kubernetes configuration #kubernetes deployment #kubernetes in development #kubernetes in production #kubernetes ingress #kubernetes interfaces #kubernetes monitoring #kubernetes networking #kubernetes observability #kubernetes plugins #kubernetes secrets #kubernetes security #kubernetes security best practices #kubernetes security vendors #kubernetes service discovery #kubernetic #kubesec #kubeterminal #kubeval #kudo #kuma #microsoft azure key vault #mozilla sops #octant #octarine #open source #palo alto kubernetes security #permission-manager #pgp #rafay #rakess #rancher #rook #secrets operations #serverless function #service mesh #shell-operator #snyk #snyk container #sonobuoy #strongdm #tcpdump #tenkai #testing #tigera #tilt #vert.x #wireshark #yaml
1596754901
The shift towards microservices and modular applications makes testing more important and more challenging at the same time. You have to make sure that the microservices running in containers perform well and as intended, but you can no longer rely on conventional testing strategies to get the job done.
This is where new testing approaches are needed. Testing your microservices applications require the right approach, a suitable set of tools, and immense attention to details. This article will guide you through the process of testing your microservices and talk about the challenges you will have to overcome along the way. Let’s get started, shall we?
Traditionally, testing a monolith application meant configuring a test environment and setting up all of the application components in a way that matched the production environment. It took time to set up the testing environment, and there were a lot of complexities around the process.
Testing also requires the application to run in full. It is not possible to test monolith apps on a per-component basis, mainly because there is usually a base code that ties everything together, and the app is designed to run as a complete app to work properly.
Microservices running in containers offer one particular advantage: universal compatibility. You don’t have to match the testing environment with the deployment architecture exactly, and you can get away with testing individual components rather than the full app in some situations.
Of course, you will have to embrace the new cloud-native approach across the pipeline. Rather than creating critical dependencies between microservices, you need to treat each one as a semi-independent module.
The only monolith or centralized portion of the application is the database, but this too is an easy challenge to overcome. As long as you have a persistent database running on your test environment, you can perform tests at any time.
Keep in mind that there are additional things to focus on when testing microservices.
Test containers are the method of choice for many developers. Unlike monolith apps, which lets you use stubs and mocks for testing, microservices need to be tested in test containers. Many CI/CD pipelines actually integrate production microservices as part of the testing process.
As mentioned before, there are many ways to test microservices effectively, but the one approach that developers now use reliably is contract testing. Loosely coupled microservices can be tested in an effective and efficient way using contract testing, mainly because this testing approach focuses on contracts; in other words, it focuses on how components or microservices communicate with each other.
Syntax and semantics construct how components communicate with each other. By defining syntax and semantics in a standardized way and testing microservices based on their ability to generate the right message formats and meet behavioral expectations, you can rest assured knowing that the microservices will behave as intended when deployed.
It is easy to fall into the trap of making testing microservices complicated, but there are ways to avoid this problem. Testing microservices doesn’t have to be complicated at all when you have the right strategy in place.
There are several ways to test microservices too, including:
What’s important to note is the fact that these testing approaches allow for asynchronous testing. After all, asynchronous development is what makes developing microservices very appealing in the first place. By allowing for asynchronous testing, you can also make sure that components or microservices can be updated independently to one another.
#blog #microservices #testing #caylent #contract testing #end-to-end testing #hoverfly #integration testing #microservices #microservices architecture #pact #testing #unit testing #vagrant #vcr
1667086140
This bundle provides a collection of annotations for Symfony2 Controllers, designed to streamline the creation of certain objects and enable smaller and more concise actions.
By default, all annotations are loaded, but any individual annotation can be completely disabled by setting to false active
parameter.
Default values are:
controller_extra:
resolver_priority: -8
request: current
paginator:
active: true
default_name: paginator
default_page: 1
default_limit_per_page: 10
entity:
active: true
default_name: entity
default_persist: true
default_mapping_fallback: false
default_factory_method: create
default_factory_mapping: true
form:
active: true
default_name: form
object_manager:
active: true
default_name: form
flush:
active: true
default_manager: default
json_response:
active: true
default_status: 200
default_headers: []
log:
active: true
default_level: info
default_execute: pre
ResolverEventListener is subscribed to
kernel.controller
event with priority -8. This element can be configured and customized withresolver_priority
config value. If you need to get ParamConverter entities, make sure that this value is lower than 0. The reason is that this listener must be executed always after ParamConverter one.
Entity provider
In some annotations, you can define an entity by several ways. This chapter is about how you can define them.
You can define an entity using its namespace. A simple new new()
be performed.
/**
* Simple controller method
*
* @SomeAnnotation(
* class = "Mmoreram\CustomBundle\Entity\MyEntity",
* )
*/
public function indexAction()
{
}
You can define an entity using Doctrine shortcut notations. With this format you should ensure that your Entities follow Symfony Bundle standards and your entities are placed under Entity/
folder.
/**
* Simple controller method
*
* @SomeAnnotation(
* class = "MmoreramCustomBundle:MyEntity",
* )
*/
public function indexAction()
{
}
You can define an entity using a simple config parameter. Some projects use parameters to define all entity namespaces (To allow overriding). If you define the entity with a parameter, this bundle will try to instance it with a simple new()
accessing directly to the container ParametersBag.
parameters:
#
# Entities
#
my.bundle.entity.myentity: Mmoreram\CustomBundle\Entity\MyEntity
/**
* Simple controller method
*
* @SomeAnnotation(
* class = "my.bundle.entity.myentity",
* )
*/
public function indexAction()
{
}
Controller annotations
This bundle provide a reduced but useful set of annotations for your controller actions.
Creates a Doctrine Paginator object, given a request and a configuration. This annotation just injects into de controller a new Doctrine\ORM\Tools\Pagination\Pagination
instance ready to be iterated.
You can enable/disable this bundle by overriding active
flag in configuration file config.yml
controller_extra:
pagination:
active: true
By default, if
name
option is not set, the generated object will be placed in a parameter named$paginator
. This behaviour can be configured usingdefault_name
in configuration.
This annotation can be configured with these sections
To create a new Pagination object you need to refer to an existing Entity. You can check all available formats you can define it just reading the Entity Provider section.
<?php
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* )
*/
public function indexAction(Paginator $paginator)
{
}
You need to specify Paginator annotation the page to fetch. By default, if none is specified, this bundle will use the default one defined in configuration. You can override in config.yml
controller_extra:
pagination:
default_page: 1
You can refer to an existing Request attribute using ~value~
format, to any $_GET
element by using format ?field?
or to any $_POST
by using format #field#
You can choose between Master Request or Current Request accessing to its attributes, by configuring the request value of the configuration.
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/{foo}
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* page = "~foo~"
* )
*/
public function indexAction(Paginator $paginator)
{
}
or you can hardcode the page to use.
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* page = 1
* )
*/
public function indexAction(Paginator $paginator)
{
}
You need to specify Paginator annotation the limit to fetch. By default, if none is specified, this bundle will use the default one defined in configuration. You can override in config.yml
controller_extra:
pagination:
default_limit_per_page: 10
You can refer to an existing Request attribute using ~value~
format, to any $_GET
element by using format ?field?
or to any $_POST
by using format #field#
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/{foo}/{limit}
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* page = "~foo~",
* limit = "~limit~"
* )
*/
public function indexAction(Paginator $paginator)
{
}
or you can hardcode the page to use.
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* page = 1,
* limit = 10
* )
*/
public function indexAction(Paginator $paginator)
{
}
You can order your Pagination just defining the fields you want to orderBy and the desired direction. The orderBy
section must be defined as an array of arrays, and each array should contain these positions:
x
)use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* orderBy = {
* {"x", "createdAt", "ASC"},
* {"x", "updatedAt", "DESC"},
* {"x", "id", 1, {
* 0 => "ASC",
* 1 => "DESC",
* }},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
With the third and fourth value you can define a map where to match your own direction nomenclature with DQL one. DQL nomenclature just accept ASC for Ascendant and DESC for Descendant.
This is very useful when you need to match a url format with the DQL one. You can refer to an existing Request attribute using ~value~
format, to any $_GET
element by using format ?field?
or to any $_POST
by using format #field#
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/order/{field}/{direction}
*
* For example, some matchings...
*
* /myroute/paginate/order/id/1 -> ORDER BY id DESC
* /myroute/paginate/order/enabled/0 - ORDER BY enabled ASC
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* orderBy = {
* {"x", "createdAt", "ASC"},
* {"x", "updatedAt", "DESC"},
* {"x", "~field~", ~direction~, {
* 0 => "ASC",
* 1 => "DESC",
* }},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
The order of the definitions will alter the order of the DQL query.
You can define some where statements in your Paginator. The wheres
section must be defined as an array of arrays, and each array should contain these positions:
x
)use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* wheres = {
* {"x", "enabled", "=", true},
* {"x", "age", ">", 18},
* {"x", "name", "LIKE", "Eferv%"},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
You can refer to an existing Request attribute using ~value~
format, to any $_GET
element by using format ?field?
or to any $_POST
by using format #field#
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/{field}
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* wheres = {
* {"x", "name", "LIKE", "~field~"},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
You can use as well this feature for optional filtering by setting the last position to true
. In that case, if the filter value is not found, such line will be ignored.
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /myroute?query=name%
* This Controller matches pattern /myroute as well
*
* In both cases this will work. In the first case we will apply the where line
* in the paginator. In the second case, we wont.
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* wheres = {
* {"x", "name", "LIKE", "?query?", true},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
You can also define some fields to not null. Is same as wheres
section, but specific for NULL assignments. The notNulls
section must be defined as an array of arrays, and each array should contain these positions:
x
)use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* notNulls = {
* {"x", "enabled"},
* {"x", "deleted"},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
You can do some left joins in this section. The leftJoins
section must be defined as an array of array, where each array can have these fields:
x
)use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* leftJoins = {
* {"x", "User", "u", true},
* {"x", "Address", "a", true},
* {"x", "Cart", "c"},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
You can do some inner joins in this section. The innerJoins
section must be defined as an array of array, where each array can have these fields:
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* innerJoins = {
* {"x", "User", "u", true},
* {"x", "Address", "a", true},
* {"x", "Cart", "c"},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
A nice feature of this annotation is that you can also inject into your controller a Mmoreram\ControllerExtraBundle\ValueObject\PaginatorAttributes
instance with some interesting information about your pagination.
To inject this object you need to define the "attributes" annotation field with the method parameter name.
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
use Mmoreram\ControllerExtraBundle\ValueObject\PaginatorAttributes;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/
*
* @CreatePaginator(
* attributes = "paginatorAttributes",
* entityNamespace = "MmoreramCustomBundle:User",
* page = 1,
* limit = 10
* )
*/
public function indexAction(
Paginator $paginator,
PaginatorAttributes $paginatorAttributes
)
{
$currentPage = $paginatorAttributes->getCurrentPage();
$totalElements = $paginatorAttributes->getTotalElements();
$totalPages = $paginatorAttributes->getTotalPages();
$limitPerPage = $paginatorAttributes->getLimitPerPage();
}
This is a completed example and its DQL resolution
use Doctrine\ORM\Tools\Pagination\Pagination;
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
/**
* Simple controller method
*
* This Controller matches pattern /paginate/nb/{limit}/{page}
*
* Where:
*
* * limit = 10
* * page = 1
*
* @CreatePaginator(
* entityNamespace = "ControllerExtraBundle:Fake",
* page = "~page~",
* limit = "~limit~",
* orderBy = {
* { "x", "createdAt", "ASC" },
* { "x", "updatedAt", "DESC" },
* { "x", "id", "0", {
* "1" = "ASC",
* "2" = "DESC",
* }}
* },
* wheres = {
* { "x", "enabled" , "=", true }
* },
* leftJoins = {
* { "x", "relation", "r" },
* { "x", "relation2", "r2" },
* { "x", "relation5", "r5", true },
* },
* innerJoins = {
* { "x", "relation3", "r3" },
* { "x", "relation4", "r4", true },
* },
* notNulls = {
* {"x", "address1"},
* {"x", "address2"},
* }
* )
*/
public function indexAction(Paginator $paginator)
{
}
The DQL generated by this annotation is
SELECT x, r4, r5
FROM Mmoreram\\ControllerExtraBundle\\Tests\\FakeBundle\\Entity\\Fake x
INNER JOIN x.relation3 r3
INNER JOIN x.relation4 r4
LEFT JOIN x.relation r
LEFT JOIN x.relation2 r2
LEFT JOIN x.relation5 r5
WHERE enabled = ?where0
AND x.address1 IS NOT NULL
AND x.address2 IS NOT NULL
ORDER BY createdAt ASC, id ASC
This annotation can create a PagerFanta instance if you need it. You only have to define your parameter as such, and the annotation resolver will wrap your paginator with a Pagerfanta object instance.
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
use Pagerfanta\Pagerfanta;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* page = 1,
* limit = 10
* )
*/
public function indexAction(Pagerfanta $paginator)
{
}
This annotation can create a KNPPaginator instance if you need it. You only have to define your parameter as such, and the annotation resolver will wrap your paginator with a KNPPaginator object instance.
use Mmoreram\ControllerExtraBundle\Annotation\CreatePaginator;
use Knp\Component\Pager\Pagination\PaginationInterface;
/**
* Simple controller method
*
* This Controller matches pattern /myroute/paginate/
*
* @CreatePaginator(
* entityNamespace = "MmoreramCustomBundle:User",
* page = 1,
* limit = 10
* )
*/
public function indexAction(PaginationInterface $paginator)
{
}
Loads an entity from your database, or creates a new one.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @Entity(
* namespace = "MmoreramCustomBundle:User",
* name = "user"
* )
*/
public function indexAction(User $user)
{
}
By default, if
name
option is not set, the generated object will be placed in a parameter named$entity
. This behaviour can be configured usingdefault_name
in configuration.
You can also use setters in Entity annotation. It means that you can simply call entity setters using Request attributes.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\Address;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @Entity(
* namespace = "MmoreramCustomBundle:Address",
* name = "address"
* )
* @Entity(
* namespace = "MmoreramCustomBundle:User",
* name = "user",
* setters = {
* "setAddress": "address"
* }
* )
*/
public function indexAction(Address $address, User $user)
{
}
When User
instance is built, method setAddress
is called using as parameter the new Address
instance.
New entities are just created with a simple new()
, so they are not persisted. By default, they will be persisted using configured manager, but you can disable this feature using persist
option.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @Entity(
* namespace = "MmoreramCustomBundle:User",
* name = "user",
* persist = false
* )
*/
public function indexAction(User $user)
{
}
When you define a new Entity annotation, you can also request the mapped entity given a map. It means that if a map is defined, this bundle will try to request the mapped instance satisfying it.
The keys of the map represent the names of the mapped fields and the values represent their desired values. Remember than you can refer to any Request attribute by using format ~field~
, to any $_GET
element by using format ?field?
or to any $_POST
by using format #field#
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* This Controller matches pattern /user/edit/{id}/{username}
*
* @Entity(
* namespace = "MmoreramCustomBundle:User",
* name = "user",
* mapping = {
* "id": "~id~",
* "username": "~username~"
* }
* )
*/
public function indexAction(User $user)
{
}
In this case, you will try to get the mapped instance of User with passed id. If some mapping is defined and any entity is found, a new EntityNotFoundException` is thrown.
So what if one ore more than one mapping references are not found? For example, you're trying to map the {id} parameter from your route, but this parameter is not even defined. Whan happens here? Well, you can assume then that you want to pass a new entity instance by using the mappingFallback.
By default, if
mapping_fallback
option is not set, the used value will be the parameterdefault_mapping_fallback
defined in configuration. By default this value isfalse
Don't confuse with the scenario where you're looking for an entity in your database, all mapping references have been resolved, and the entity is not found. In that case, a common "EntityNotFound" exception will be thrown by Doctrine.
Lets see an example. Because we have enabled the mappingFallback, and because the mapping definition does not match the assigned route, we will return a new empty User entity.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Entity;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* This Controller matches pattern /user/edit/{id}
*
* @LoadEntity(
* namespace = "MmoreramCustomBundle:User",
* name = "user",
* mapping = {
* "id": "~id~",
* "username": "~nonexisting~"
* },
* mappingFallback = true
* )
*/
public function indexAction(User $user)
{
// $user->getId() === null
}
By default, the Doctrine entity manager provides the right repository per each entity (not the default one, but the right specific one). Although, you can define a custom repository to be used in your annotation by using the repository configuration.
/**
* Simple controller method
*
* @CreateEntity(
* namespace = "MmoreramCustomBundle:User",
* mapping = {
* "id": "~id~",
* "username": "~username~"
* }
* repository = {
* "class" = "Mmoreram\CustomBundle\Repository\AnotherRepository",
* },
* )
*/
public function indexAction(User $user)
{
}
By default, the method findOneBy will always be used, unless you define another one.
/**
* Simple controller method
*
* @CreateEntity(
* namespace = "MmoreramCustomBundle:User",
* mapping = {
* "id": "~id~",
* "username": "~username~"
* }
* repository = {
* "class" = "Mmoreram\CustomBundle\Repository\AnotherRepository",
* "method" = "find",
* },
* )
*/
public function indexAction(User $user)
{
}
When the annotation considers that a new entity must be created, because no mapping information has been provided, or because the mapping fallback has been activated, by default a new instance will be created by using the namespace value.
This configuration block has three positions
You can define the factory with a simple namespace
/**
* Simple controller method
*
* @CreateEntity(
* namespace = "MmoreramCustomBundle:User",
* factory = {
* "class" = "Mmoreram\CustomBundle\Factory\UserFactory",
* "method" = "create",
* "static" = true,
* },
* )
*/
public function indexAction(User $user)
{
}
If you want to define your Factory as a service, with the possibility of overriding namespace, you can simply define service name. All other options have the same behaviour.
parameters:
#
# Factories
#
my.bundle.factory.user_factory: Mmoreram\CustomBundle\Factory\UserFactory
/**
* Simple controller method
*
* @CreateEntity(
* class = {
* "factory" = my.bundle.factory.user_factory,
* "method" = "create",
* "static" = true,
* },
* )
*/
public function indexAction(User $user)
{
}
If you do not define the method
, default one will be used. You can override this default value by defining new one in your config.yml
. Same with static
value
controller_extra:
entity:
default_factory_method: create
default_factory_static: true
Provides form injection in your controller actions. This annotation only needs a name to be defined in, where you must define namespace where your form is placed.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Symfony\Component\Form\AbstractType;
/**
* Simple controller method
*
* @CreateForm(
* class = "\Mmoreram\CustomBundle\Form\Type\UserType",
* name = "userType"
* )
*/
public function indexAction(AbstractType $userType)
{
}
By default, if
name
option is not set, the generated object will be placed in a parameter named$form
. This behaviour can be configured usingdefault_name
in configuration.
You can not just define your Type location using the namespace, in which case a new AbstractType element will be created. but you can also define it using service alias, in which case this bundle will return an instance using Symfony DI.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Symfony\Component\Form\AbstractType;
/**
* Simple controller method
*
* @CreateForm(
* class = "user_type",
* name = "userType"
* )
*/
public function indexAction(AbstractType $userType)
{
}
This annotation allows you to not only create an instance of FormType, but also allows you to inject a Form object or a FormView object
To inject a Form object you only need to cast method value as such.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Symfony\Component\Form\Form;
/**
* Simple controller method
*
* @CreateForm(
* class = "user_type",
* name = "userForm"
* )
*/
public function indexAction(Form $userForm)
{
}
You can also, using [SensioFrameworkExtraBundle][1]'s [ParamConverter][2], create a Form object with an previously created entity. you can define this entity using entity
parameter.
<?php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Form\Form;
use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @Route(
* path = "/user/{id}",
* name = "view_user"
* )
* @ParamConverter("user", class="MmoreramCustomBundle:User")
* @CreateForm(
* class = "user_type",
* entity = "user"
* name = "userForm",
* )
*/
public function indexAction(User $user, Form $userForm)
{
}
To handle current request, you can set handleRequest
to true. By default this value is set to false
<?php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Form\Form;
use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @Route(
* path = "/user/{id}",
* name = "view_user"
* )
* @ParamConverter("user", class="MmoreramCustomBundle:User")
* @CreateForm(
* class = "user_type",
* entity = "user"
* handleRequest = true,
* name = "userForm",
* )
*/
public function indexAction(User $user, Form $userForm)
{
}
You can also add as a method parameter if the form is valid, using validate
setting. Annotation will place result of $form->isValid()
in specified method argument.
<?php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Symfony\Component\Form\Form;
use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @Route(
* path = "/user/{id}",
* name = "view_user"
* )
* @ParamConverter("user", class="MmoreramCustomBundle:User")
* @CreateForm(
* class = "user_type",
* entity = "user"
* handleRequest = true,
* name = "userForm",
* validate = "isValid",
* )
*/
public function indexAction(User $user, Form $userForm, $isValid)
{
}
To inject a FormView object you only need to cast method variable as such.
<?php
use Symfony\Component\Form\FormView;
use Mmoreram\ControllerExtraBundle\Annotation\CreateForm;
/**
* Simple controller method
*
* @CreateForm(
* class = "user_type",
* name = "userFormView"
* )
*/
public function indexAction(FormView $userFormView)
{
}
Flush annotation allows you to flush entityManager at the end of request using kernel.response event
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Flush;
/**
* Simple controller method
*
* @Flush
*/
public function indexAction()
{
}
If not otherwise specified, default Doctrine Manager will be flushed with this annotation. You can overwrite default Manager in your config.yml
file.
controller_extra:
flush:
default_manager: my_custom_manager
You can also override this value in every single Flush Annotation instance defining manager
value
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Flush;
/**
* Simple controller method
*
* @Flush(
* manager = "my_own_manager"
* )
*/
public function indexAction()
{
}
If you want to change default manager in all annotation instances, you should override bundle parameter in your config.yml
file.
controller_extra:
flush:
default_manager: my_own_manager
If any parameter is set, annotation will flush all. If you only need to flush one or many entities, you can define explicitly which entity must be flushed.
<?php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Mmoreram\ControllerExtraBundle\Annotation\Flush;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @ParamConverter("user", class="MmoreramCustomBundle:User")
* @Flush(
* entity = "user"
* )
*/
public function indexAction(User $user)
{
}
You can also define a set of entities to flush
<?php
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Mmoreram\ControllerExtraBundle\Annotation\Flush;
use Mmoreram\ControllerExtraBundle\Entity\Address;
use Mmoreram\ControllerExtraBundle\Entity\User;
/**
* Simple controller method
*
* @ParamConverter("user", class="MmoreramCustomBundle:User")
* @ParamConverter("address", class="MmoreramCustomBundle:Address")
* @Flush(
* entity = {
* "user",
* "address"
* }
* )
*/
public function indexAction(User $user, Address $address)
{
}
If multiple @Mmoreram\Flush are defined in same action, last instance will overwrite previous. Anyway just one instance should be defined.
JsonResponse annotation allows you to create a Symfony\Component\HttpFoundation\JsonResponse
object, given a simple controller return value.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\ToJsonResponse;
/**
* Simple controller method
*
* @ToJsonResponse
*/
public function indexAction(User $user, Address $address)
{
return array(
'This is my response'
);
}
By default, JsonResponse is created using default status
and headers
defined in bundle parameters. You can overwrite them in your config.yml
file.
controller_extra:
json_response:
default_status: 403
default_headers:
"User-Agent": "Googlebot/2.1"
You can also overwrite these values in each @JsonResponse
annotation.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\ToJsonResponse;
/**
* Simple controller method
*
* @ToJsonResponse(
* status = 403,
* headers = {
* "User-Agent": "Googlebot/2.1"
* }
* )
*/
public function indexAction(User $user, Address $address)
{
return array(
'This is my response'
);
}
If an Exception is returned the response status is set by default to 500 and the Exception message is returned as response.
STATUS 500 Internal server error
{
message : 'Exception message'
}
In case we use a HttpExceptionInterface the use the exception status code as status code. In case we launch this exception
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
...
return new NotFoundHttpException('Resource not found');
We'll receive this response
STATUS 404 Not Found
{
message : 'Resource not found'
}
If the exception is being launched on an annotation (e.g. Entity annotation) remember to add the JsonResponse annotation at the beginning or at least before any annotation that could cause an exception.
If multiple @Mmoreram\JsonResponse are defined in same action, last instance will overwrite previous. Anyway just one instance should be defined.
Log annotation allows you to log any plain message before or after controller action execution
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Log;
/**
* Simple controller method
*
* @Log("Executing index Action")
*/
public function indexAction()
{
}
You can define the level of the message. You can define default one if none is specified overriding it in your config.yml
file.
controller_extra:
log:
default_level: warning
Every Annotation instance can overwrite this value using level
field.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Log;
/**
* Simple controller method
*
* @Log(
* value = "Executing index Action",
* level = @Log::LVL_WARNING
* )
*/
public function indexAction()
{
}
Several levels can be used, as defined in [Psr\Log\LoggerInterface][6] interface
You can also define the execution of the log. You can define default one if none is specified overriding it in your config.yml
file.
controller_extra:
log:
default_execute: pre
Every Annotation instance can overwrite this value using level
field.
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Log;
/**
* Simple controller method
*
* @Log(
* value = "Executing index Action",
* execute = @Log::EXEC_POST
* )
*/
public function indexAction()
{
}
Several executions can be used,
The Get annotation allows you to get any parameter from the request query string.
For a GET
request like:
GET /my-page?foo=bar HTTP/1.1
You can can simply get the foo
var using the GET
annotation
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Get;
/**
* Simple controller method
*
* @Get(
* path = "foo"
* )
*/
public function indexAction($foo)
{
// Use the foo var
}
You can also customize the var name and the default value in case the var is not sent on the query string.
For a GET
request like:
GET /my-page HTTP/1.1
And this annotation
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Get;
/**
* Simple controller method
*
* @Get(
* path = "foo",
* name = "varName",
* default = 'bar',
* )
*/
public function indexAction($varName)
{
// This would print 'bar'
echo $varName;
}
The Post annotation allows you to get any parameter from the post request body.
For a POST
request like:
POST /my-page HTTP/1.1
foo=bar
You can can simply get the foo
var using the POST
annotation
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Post;
/**
* Simple controller method
*
* @Post(
* path = "foo"
* )
*/
public function indexAction($foo)
{
// Use the foo var
}
You can also customize the var name and the default value in case the var is not sent on the query string.
For a POST
request like:
POST /my-page HTTP/1.1
And this annotation
<?php
use Mmoreram\ControllerExtraBundle\Annotation\Post;
/**
* Simple controller method
*
* @Post(
* path = "foo",
* name = "varName",
* default = 'bar',
* )
*/
public function indexAction($varName)
{
// This would print 'bar'
echo $varName;
}
Custom annotations
Using this bundle you can now create, in a very easy way, your own controller annotation.
The annotation object. You need to define the fields your custom annotation will contain. Must extends Mmoreram\ControllerExtraBundle\Annotation\Annotation
abstract class.
<?php
namespace My\Bundle\Annotation;
use Mmoreram\ControllerExtraBundle\Annotation\Annotation;
/**
* Entity annotation driver
*
* @Annotation
* @Target({"METHOD"})
*/
final class MyCustomAnnotation extends Annotation
{
/**
* @var string
*
* Dummy field
*/
public $field;
/**
* Get Dummy field
*
* @return string Dummy field
*/
public function getField()
{
return $this->field;
}
}
Once you have defined your own annotation, you have to resolve how this annotation works in a controller. You can manage this using a Resolver. Must extend Mmoreram\ControllerExtraBundle\Resolver\AnnotationResolver;
abstract class.
<?php
namespace My\Bundle\Resolver;
use Symfony\Component\HttpFoundation\Request;
use Mmoreram\ControllerExtraBundle\Resolver\AnnotationResolver;
use Mmoreram\ControllerExtraBundle\Annotation\Annotation;
/**
* MyCustomAnnotation Resolver
*/
class MyCustomAnnotationResolver extends AnnotationResolver
{
/**
* Specific annotation evaluation.
*
* This method must be implemented in every single EventListener
* with specific logic
*
* All method code will executed only if specific active flag is true
*
* @param Request $request
* @param Annotation $annotation
* @param ReflectionMethod $method
*/
public function evaluateAnnotation(
Request $request,
Annotation $annotation,
ReflectionMethod $method
)
{
/**
* You can now manage your annotation.
* You can access to its fields using public methods.
*
* Annotation fields can be public and can be acceded directly,
* but is better for testing to use getters; they can be mocked.
*/
$field = $annotation->getField();
/**
* You can also access to existing method parameters.
*
* Available parameters are:
*
* # ParamConverter parameters ( See `resolver_priority` config value )
* # All method defined parameters, included Request object if is set.
*/
$entity = $request->attributes->get('entity');
/**
* And you can now place new elements in the controller action.
* In this example we are creating new method parameter
* called $myNewField with some value
*/
$request->attributes->set(
'myNewField',
new $field()
);
return $this;
}
}
This class will be defined as a service, so this method is computed just before executing current controller. You can also subscribe to some kernel events and do whatever you need to do ( You can check Mmoreram\ControllerExtraBundle\Resolver\LogAnnotationResolver
for some examples.
Once Resolver is done, we need to define our service as an Annotation Resolver. We will use a custom tag
.
parameters:
#
# Resolvers
#
my.bundle.resolver.my_custom_annotation_resolver.class: My\Bundle\Resolver\MyCustomAnnotationResolver
services:
#
# Resolvers
#
my.bundle.resolver.my_custom_annotation_resolver:
class: %my.bundle.resolver.my_custom_annotation_resolver.class%
tags:
- { name: controller_extra.annotation }
We need to register our annotation inside our application. We can just do it in the boot()
method of bundle.php
file.
<?php
namespace My\Bundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
use Doctrine\Common\Annotations\AnnotationRegistry;
/**
* MyBundle
*/
class ControllerExtraBundle extends Bundle
{
/**
* Boots the Bundle.
*/
public function boot()
{
$kernel = $this->container->get('kernel');
AnnotationRegistry::registerFile($kernel
->locateResource("@MyBundle/Annotation/MyCustomAnnotation.php")
);
}
}
Et voilà! We can now use our custom Annotation in our project controllers.
Author: mmoreram
Source Code: https://github.com/mmoreram/ControllerExtraBundle
License: MIT license
1620983255
Automation and segregation can help you build better software
If you write automated tests and deliver them to the customer, he can make sure the software is working properly. And, at the end of the day, he paid for it.
Ok. We can segregate or separate the tests according to some criteria. For example, “white box” tests are used to measure the internal quality of the software, in addition to the expected results. They are very useful to know the percentage of lines of code executed, the cyclomatic complexity and several other software metrics. Unit tests are white box tests.
#testing #software testing #regression tests #unit tests #integration tests
1599859380
Nowadays API testing is an integral part of testing. There are a lot of tools like postman, insomnia, etc. There are many articles that ask what is API, What is API testing, but the problem is How to do API testing? What I need to validate.
Note: In this article, I am going to use postman assertions for all the examples since it is the most popular tool. But this article is not intended only for the postman tool.
Let’s directly jump to the topic.
Let’s consider you have an API endpoint example http://dzone.com/getuserDetails/{{username}} when you send the get request to that URL it returns the JSON response.
My API endpoint is http://dzone.com/getuserDetails/{{username}}
The response is in JSON format like below
JSON
{
"jobTitle": "string",
"userid": "string",
"phoneNumber": "string",
"password": "string",
"email": "user@example.com",
"firstName": "string",
"lastName": "string",
"userName": "string",
"country": "string",
"region": "string",
"city": "string",
"department": "string",
"userType": 0
}
In the JSON we can see there are properties and associated values.
Now, For example, if we need details of the user with the username ‘ganeshhegde’ we need to send a **GET **request to **http://dzone.com/getuserDetails/ganeshhegde **
Now there are two scenarios.
1. Valid Usecase: User is available in the database and it returns user details with status code 200
2. Invalid Usecase: User is Unavailable/Invalid user in this case it returns status with code 404 with not found message.
#tutorial #performance #api #test automation #api testing #testing and qa #application programming interface #testing as a service #testing tutorial #api test