Why doesn't #JsonUnwrapped work for Lists? - jackson

I am using Jackson 2.1.0. Given:
public static final class GetCompanies
{
private final List<URI> companies;
/**
* Creates a new GetCompanies.
* <p/>
* #param companies the list of available companies
* #throws NullPointerException if companies is null
*/
#JsonCreator
public GetCompanies(#JsonUnwrapped #NotNull List<URI> companies)
{
Preconditions.checkNotNull(companies, "companies");
this.companies = ImmutableList.copyOf(companies);
}
/**
* #return the list of available companies
*/
#JsonUnwrapped
#SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<URI> getCompanies()
{
return companies;
}
}
When the input list contains http://test.com/, Jackson generates:
{"companies":["http://test.com/"]}
instead of:
["http://test.com/"]
Any ideas?
UPDATE: See https://github.com/FasterXML/jackson-core/issues/41 for a related discussion.

In this case, if this was to work, you'd end up trying to produce following:
{ "http://test.com" }
which is not legal JSON. #JsonUnwrapped really just removes one layer of wrapping. And although it theoretically could be made to work for "arrays in arrays" case, it does not.
And in fact I wonder if adding this feature was a mistake: mostly because it encourages use that is often against data-binding best practices (simplicity, one-to-one mapping).
But what would work instead is #JsonValue:
#JsonValue
private final List<URI> companies;
which means "use value of this property instead of serializing the object that contains it".
And the creator method would actually work as-is, no need for either #JsonUnwrapped or #JsonProperty.
Here is the corrected code:
public static final class GetCompanies
{
private final List<URI> companies;
/**
* Creates a new GetCompanies.
* <p/>
* #param companies the list of available companies
* #throws NullPointerException if companies is null
*/
#JsonCreator
public GetCompanies(#NotNull List<URI> companies)
{
Preconditions.checkNotNull(companies, "companies");
this.companies = ImmutableList.copyOf(companies);
}
/**
* #return the list of available companies
*/
#JsonValue
#SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<URI> getCompanies()
{
return companies;
}
}

Related

API Platform - Entity Translation, Doctrine Translatable

I'm trying to add KnpLabs Doctrine Behaviors - and precisely, the Translatable Behavior - on one of the entity in my API Platform project.
Here's what I've done so far :
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use ApiPlatform\Core\Annotation\ApiResource;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
* #ApiResource
*/
class Article
{
use ORMBehaviors\Translatable\Translation,
ORMBehaviors\Timestampable\Timestampable
;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
}
And here's the Entity translation :
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Traits as CustomTraits;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleTranslationRepository")
* #ApiResource
*/
class ArticleTranslation
{
use ORMBehaviors\Translatable\Translatable;
/**
* #var string
*
* #ORM\Column(name="someFieldToTranslate", type="string", length=255, nullable=true)
*/
private $someFieldToTranslate;
public function getSomeFieldToTranslate(){...}
public function setSomeFieldToTranslate($someFieldToTranslate){...}
}
Here's the basic "configuration" for getting Translatable Behavior working according to the doc.
Issues start when I try to update the DB schema : I got this error:
No identifier/primary key specified for Entity "App\Entity\ArticleTranslation". Every Entity must have an identifier/primary key in . (which is being imported from "/Sites/bookshop-api/config/routes/api_platform.yaml"). Make sure there is a
loader supporting the "api_platform" type.
However in Translatable Traits, there's already an ID and documentation precise that Translation Entity should only have fields we want to translate...
Anyway, I've put an ID to this ArtcleTranslation Entity to get rid of the error :
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Traits as CustomTraits;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleTranslationRepository")
* #ApiResource
*/
class ArticleTranslation
{
use ORMBehaviors\Translatable\Translatable;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="content", type="string", length=255, nullable=true)
*/
private $content;
public function getContent(){...}
public function setContent($someContent){...}
}
From here, no error when I update the DB schema. Perfect !
Now I can take a look at the Swagger Documentation :
Everything looks fine ! But when I do take a look at the DB :
In Article table :
no "local" field
no "empty" field
In ArticleTranslation table :
no "translatable_id" field
no "currentLocal"
no "defaultLocal"
I guess it must be linked but in the swagger POST Tab, the model is different too.
article_translation model
I only tried /GET and /POST method on both entities, they're working (I can see it in DB) but no relation between the 2 of them.
I hope my post is not too long but I tried to be the more specific !
Thanks in advance
I did another answer as the first question was to resolve a mistake and not to explain how I integrated Knp Labs Doctrine Translations with Api Platform.
Summary :
We have an entity Article which has some fields translated inside an ArticleTranslation. We want to retrieve the entity through Api platform with its translations and we want to add or update translations through the api.
What I did :
1 - Article entity has a use ORMBehaviors\Translatable\Translatable. If we look inside of this trait, it has 2 attributes : $translations and $newTranslations. We need to expose these attributes inside the Article entity :
class Article {
use ORMBehaviors\Translatable\Translatable;
protected $translations;
protected $newTranslations;
}
Now we have a new attribute translations which is automatically filled from ArticleTranslation when we are getting some Article from the api
2 - Now we want to add/edit some translations : We need to fill the newTranslations attribute inside the Article when we are sending to the api:
"newTranslations": {
"en": {
"description": "Firstname"
},
"fr": {
"description": "Prénom"
}
}
Now we are receiving the new translations into the api but it's not persisted because we have to call the function mergeNewTranslations(). This function just take all translations inside the attribute $newTranslations and merge it with the $translations attribute in order to persist it.
3 - I created a new trait that I called TranslatableOverride. I imported it on directly on my Entity next to ORMBehaviors\Translatable\Translation:
trait TranslatableOverride
{
/**
* Set collection of new translations.
*
* #return ArrayCollection
*/
public function setNewTranslations($newTranslations)
{
if ($newTranslations) {
foreach ($newTranslations as $locale => $translations) {
foreach ($translations as $key => $value) {
$tr = $this->translate($locale);
$setter = 'set' . ucfirst($key);
if (method_exists($tr, $setter)) {
$tr->{$setter}($value);
}
}
}
$this->mergeNewTranslations();
}
}
}
I'm not sure if it's pretty but it works like a charm with api-platform.
I didn't think about getting only one translation at a time. For the moment, I retrieve my entities with the whole bunch of translations which is definitely not efficient. I will add an override for the getTranslations in my override trait I guess.
I think you did a mistake. You have an entity Article which should be the translatable and you want to translate some fields which will be the translations?
So you should make the opposite, put the use ORMBehaviors\Translatable\Translatableon the Article and the use ORMBehaviors\Translatable\Translation on the ArticleTranslation
The fix for your Article entity :
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use ApiPlatform\Core\Annotation\ApiResource;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleRepository")
* #ApiResource
*/
class Article
{
use ORMBehaviors\Translatable\Translatable,
ORMBehaviors\Timestampable\Timestampable
;
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
protected $id;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
}
and the fix for your ArticleTranslation entity :
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
use ApiPlatform\Core\Annotation\ApiResource;
use App\Traits as CustomTraits;
/**
* #ORM\Entity(repositoryClass="App\Repository\ArticleTranslationRepository")
* #ApiResource
*/
class ArticleTranslation
{
use ORMBehaviors\Translatable\Translation;
/**
* #var string
*
* #ORM\Column(name="someFieldToTranslate", type="string", length=255, nullable=true)
*/
private $someFieldToTranslate;
public function getSomeFieldToTranslate(){...}
public function setSomeFieldToTranslate($someFieldToTranslate){...}
}
Let me know if everything is alright now.

Spring Data Rest ResourceProcessor not applied on Projections

I am using a ResourceProcessor to add additional links to my resource object when listed in a collection or fetched individually. However, when I apply a projection (or an excerpt project) to my repository, the ResourceProcessor does not get run and thus my links for that resource do not get created. Is there a means to allow my custom resource links to be added to a resource regardless of how the resource content is projected?
I think this issue is describing your case:
https://jira.spring.io/browse/DATAREST-713
Currently, spring-data-rest does not offer functionality to solve your problem.
We are using a little workaround that still needs a separate ResourceProcessor for each projection but we do not need to duplicate the link logic:
We have a base class that is able to get the underlying Entity for a Projection and invokes the Entity's ResourceProcessor and applies the links to the Projection.
Entity is a common interface for all our JPA entities - but I think you could also use org.springframework.data.domain.Persistable or org.springframework.hateoas.Identifiable.
/**
* Projections need their own resource processors in spring-data-rest.
* To avoid code duplication the ProjectionResourceProcessor delegates the link creation to
* the resource processor of the underlying entity.
* #param <E> entity type the projection is associated with
* #param <T> the resource type that this ResourceProcessor is for
*/
public class ProjectionResourceProcessor<E extends Entity, T> implements ResourceProcessor<Resource<T>> {
private final ResourceProcessor<Resource<E>> entityResourceProcessor;
public ProjectionResourceProcessor(ResourceProcessor<Resource<E>> entityResourceProcessor) {
this.entityResourceProcessor = entityResourceProcessor;
}
#SuppressWarnings("unchecked")
#Override
public Resource<T> process(Resource<T> resource) {
if (resource.getContent() instanceof TargetAware) {
TargetAware targetAware = (TargetAware) resource.getContent();
if (targetAware != null
&& targetAware.getTarget() != null
&& targetAware.getTarget() instanceof Entity) {
E target = (E) targetAware.getTarget();
resource.add(entityResourceProcessor.process(new Resource<>(target)).getLinks());
}
}
return resource;
}
}
An implementation of such a resource processor would look like this:
#Component
public class MyProjectionResourceProcessor extends ProjectionResourceProcessor<MyEntity, MyProjection> {
#Autowired
public MyProjectionResourceProcessor(EntityResourceProcessor resourceProcessor) {
super(resourceProcessor);
}
}
The implementation itself just passes the ResourceProcessor that can handle the entity class and passes it to our ProjectionResourceProcessor. It does not contain any link creation logic.
Here is a generic solution:
#Component
public class ProjectionProcessor implements RepresentationModelProcessor<EntityModel<TargetAware>> {
private final RepresentationModelProcessorInvoker processorInvoker;
public ProjectionProcessor(#Lazy RepresentationModelProcessorInvoker processorInvoker) {
this.processorInvoker = processorInvoker;
}
#Override
public EntityModel<TargetAware> process(EntityModel<TargetAware> entityModel) {
TargetAware content = entityModel.getContent();
if (content != null) {
entityModel.add(processorInvoker.invokeProcessorsFor(EntityModel.of(content.getTarget())).getLinks());
}
return entityModel;
}
}
It gets links for original entities and adds them to corrseponding projections.

JUnit Test against an interface without having the implementation yet

I try to write a test for a given interface like that with JUnit and have no idea how to do that:
public interface ShortMessageService {
/**
* Creates a message. A message is related to a topic
* Creates a date for the message
* #throws IllegalArgumentException, if the message is longer then 255 characters.
* #throws IllegalArgumentException, if the message ist shorter then 10 characters.
* #throws IllegalArgumentException, if the user doesn't exist
* #throws IllegalArgumentException, if the topic doesn't exist
* #throws NullPointerException, if one argument is null.
* #param userName
* #param message
* #return ID of the new created message
*/
Long createMessage(String userName, String message, String topic);
[...]
}
I tried to mock the interface after I realized that it doesn't make sense at all so I am a bit lost. Maybe someone can give me a good approach I can work with. I also heard about junit parameterized tests but I am not sure if that is what I am looking for.
Many thanks!
I use the following pattern to write abstract tests against my interface APIs without having any implementations available. You can write whatever tests you require in AbstractShortMessageServiceTest without having to implement them at that point in time.
public abstract class AbstractShortMessageServiceTest
{
/**
* #return A new empty instance of an implementation of FooManager.
*/
protected abstract ShortMessageService getNewShortMessageService();
private ShortMessageService testService;
#Before
public void setUp() throws Exception
{
testService = getNewShortMessageService();
}
#Test
public void testFooBar() throws Exception
{
assertEquals("question", testService.createMessage(
"DeepThought", "42", "everything"));
}
}
When you have an implementation, you can use the test simply by defining a new test class that overrides AbstractShortMessageServiceTest and implements the getNewShortMessageService method.
public class MyShortMessageServiceTest extends AbstractShortMessageServiceTest
{
protected ShortMessageService getNewShortMessageService()
{
return new MyShortMessageService();
}
}
In addition, if you need the test to be parameterized, you can do that in AbstractShortMessageServiceTest without doing it in each of the concrete tests.
Usually test is prepared for class that implements the interface and mocks are used for cooperating classes but you can test your test by mock if the class is not ready yet. It is unusual and you should use thenAnsfer with implemented logic of possible cases:
Better way is simply prepare tests for the implementation class and start to improve it till all test passes:
Implementing class can be in field and initialized before tests
private ShortMessageService testedClasOrMock;
//version with implementing class
#Before
public void setUp(){
testedClasOrMock = new ShortMessageServiceImpl0();
}
#Before
public void setUp(){
testedClasOrMock = mock(ShortMessageService.class);
when(testedClasOrMock).thenAnswer(new Answer<Long>(){
#Override
public Long answer(InvocationOnMock invocation) throws Throwable {
String message =(String) invocation.getArguments()[1];
if (message.length() > 256){
throw new IllegalArgumentException("msg is too long");
}
//other exception throwing cases
…...
return new Long(44);
}});
}
so you will have several test with expected exceptions like
#Test (expected= IllegalArgumentException.class)
public void testTooLongMsg(){
testedClasOrMock.createMessage(USER, TOO_LONG_MSG, TOPIC);
}
and one that simply should not throw exception and for instance check that msg ids are different
#Test
public void testTooLongMsg(){
long id0 = testedClasOrMock.createMessage(USER, TOO_LONG_MSG, TOPIC);
long id1 = testedClasOrMock.createMessage(USER, TOO_LONG_MSG, TOPIC);
assertTrue(id0 != id1);
}
If you insist on testing your test by mock let me know and I will add example for one test case.

TYPO3 - extending an extbase extension with new fields and using these in fluid templates

I'm trying to extend powermail (version 2) with the possibility to add a note for each input field. So far I have created a new extension using extension builder and with a few modifications to ext_tables.php the field show up in the backend. The new field is called 'note' and I thought I could just do something like {field.note} in the fluid template input.html, but that does not work. My model includes the setter and getter:
class Tx_Formnotes_Domain_Model_Powermailnotes extends Tx_Extbase_DomainObject_AbstractEntity {
/**
* note
*
* #var string
*/
protected $note;
/**
* Returns the note
*
* #return string $note
*/
public function getNote() {
return $this->note;
}
/**
* Sets the note
*
* #param string $note
* #return void
*/
public function setNote($note) {
$this->note = $note;
}
}
What else is needed?
Info: I'm using TYPO3 4.7
You could map the powermail model like
config.tx_extbase.persistence.classes {
Tx_Formnotes_Domain_Model_Powermailnotes {
mapping {
tableName = powermailTableName
columns {
exampleMedia.mapOnProperty = media
}
}
}
}
after that you should extend your TCA with these properties. At least you can write setter and getter for each property and use them in your fluid template.

Doctrine2: efficient mapping of inherited class

I've a problem figuring out how to configure the mapping of my classes with Doctrine2.
Let say I've these tables:
Address table
---------------------
- id
- civic_no
- road
- state
- country
PersonnalAddress table
---------------------
- id
- base_address_id
- type
- is_primary
BusinessAddress table
---------------------
- id
- base_address_id
- business_name
- shipping_phone
- is_primary
And thoses PHP objects:
class Address{}
class BusinessAddress extends Address{}
class PersonalAddress extends Address{}
Considering the following requirements:
An address can exist by itself (the Address class is not abstract)
A personalAddress and a businessAddress can have the very same address data
If I delete or edit the address, it has an impact on all the business or personal address that are inherited from it.
I don't want any data duplication in the database (this is a requirement of the 2nd normal form)
Proxy methods mean code duplication, I prefer not to have any.
Magic methods are not good in term of testability, I prefer not to have any.
To better illustrate the problem, I expect the data in the data base to look like:
Address table:
id | civic_no | road | state | country
1 123 test qc ca
PersonnalAddress table:
id | base_address_id | type | is_primary
1 1 A 0
2 1 B 1
BusinessAddress table:
id | base_address_id | business_name | shipping_phone | is_primary
1 1 chic choc 1231234 1
What would be the best strategy to implement a solution that match theses requirements ?
Ok this is a bit of a long one but I think it covers all your bases, if you have any questions then feel free to ask.
This comes with a caveat that I don't know if you can do Many-To-One on a MappedSuperclass. If that isn't possible then you may be able to use Class Table Inheritance instead. Give it a try and tell us if it works.
Keep in mind I pushed this code out pretty quickly, it is untested so it may not be correct but hopefully you'll get the idea of how it works.
Here we go!
Interface to make it easier to say "what is an address" without making abstract classes then overriding methods and causing the "bad design" feeling ;)
interface Address {
function getCivicNo();
function getRoad();
function getState();
function getCountry();
}
Abstract Entity which you don't really need but if you dont use it you need to duplicate the ID code.
/**
* #ORM\MappedSuperclass
*/
abstract class AbstractEntity {
/**
* Entity ID column.
*
* #var integer
*
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
public function getId() {
return $id;
}
}
BasicAddress which you can store alone or have linked to a "ComplexAddress"
/**
* #ORM\Entity
*/
class BasicAddress extends AbstractEntity implements Address {
/** #ORM\Column() */
private $road;
public function getRoad() {
return $this->road;
}
// etc etc
}
"ComplexAddress" is just here to let you re-use the code for delegating calls to a basic address.
/**
* #ORM\MappedSuperclass
*/
abstract class ComplexAddress extends AbstractEntity implements Address {
/** #ORM\Many-To-One(targetEntity="BasicAddress")
private $basicAddress;
public function __construct(BasicAddress $basicAddress) {
$this->basicAddress = $basicAddress;
}
public function getRoad() {
return $this->basicAddress->getRoad();
}
// other methods for implementing "Address" just delegate to BasicAddress
}
PublicAddress
/**
* #ORM\Entity()
*/
class PersonalAddress extends ComplexAddress {
/** #ORM\Column(type="boolean") */
private $isPrimary;
public function isPrimary() {
return $isPrimary;
}
// other personal address methods here
}
BusinessAddress
/**
* #ORM\Entity()
*/
class BusinessAddress extends ComplexAddress {
/** #ORM\Column() */
private $businessName;
public function getBusinessName() {
return $this->businessName;
}
// other business address methods here
}
Edit: Just noticed I forgot to put cascade parameters for deletion, you might need to handle this directly though - when a BasicAddress is deleted also delete the other addresses that use it.
Instead of extending the Address class, you'll have to create a OneToMany relationship on the Address and a ManyToOne relationship on the PersonalAddress and BusinessAddress. Something like this:
<?php
// ...
use Doctrine\Common\Collections\ArrayCollection;
// ...
class Address
{
// ...
/**
* #ORM\OneToMany(targetEntity="PersonalAddress", mappedBy="address")
*/
private $personalAddresses;
/**
* #ORM\OneToMany(targetEntity="BusinessAddress", mappedBy="address")
*/
private $businessAddresses;
public function __construct()
{
$this->personalAddresses = new ArrayCollection();
$this->businessAddresses = new ArrayCollection();
}
// ...
}
And, for the child classes:
<?php
// ...
// ...
class PersonalAddress
{
// ...
/**
* #ORM\ManyToOne(targetEntity="Address", inversedBy="personalAddresses")
* #ORM\JoinColumn(name="base_address_id", referencedColumnName="id")
*/
private $address;
// ...
}