Closed dbourdette closed 12 years ago
J'ai un problème avec les constructeurs de Groovy à partir d'un JSON, dans la doc il est indiqué que le Marshalling JSON est automatique lorsqu'on indique "resource" dans l'URLMapping mais j'avais à chaque fois une erreur comme quoi le constructeur à partir d'une string était inconnu. J'ai fait des tests dans Document et j'ai fini par réussir en prenant un détour par un JSONArray mais déjà ce n'est pas joli du tout et en plus cela ne fonctionne pas pour Action
De plus, je suis allée faire un tour dans ma base de test MongoDB et j'ai l'impression que tout n'est pas bien sauvé : l'objet Object a ses properties à null, alors qu'on lui fournit une liste dans le test de persistence et tous les objets Action ne contiennent qu'une date, alors qu'ils devraient également contenir un profil et un object
J'ai regardé et je ne trouve pas non plus. La doc semble plus que flou et cela ne fonctionne pas en l'état. Je regarderai demain, vous pouvez essayer en attendant.
Bon, après encore plus de tests, ça veut pas. De plus, en lisant la doc, le format du json ne semble pas très flexible (par exemple, le nom de la classe de base doit être dans le json). On va donc se tourner vers la lib de reférence pour le mapping java <-> json : jackson
On change donc de plan :
Voici un exemple simple trier le la :
import org.codehaus.jackson.map.ObjectMapper
class User
{
static class Name{
String first;
String last;
}
Name name
Boolean verified
String gender
byte[] userImage
}
String json = """{
"name" : { "first" : "Joe", "last" : "Sixpack" },
"gender" : "MALE",
"verified" : false,
"userImage" : "Rm9vYmFyIQ=="
}
"""
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(json, User.class);
assert user.name.first == "Joe"
assert user.name.last == "Sixpack"
assert user.gender == "MALE"
assert user.verified == false
assert user.userImage.size() == 7
Au résultat, de toutes façons, cela va nous offrir beaucoup plus de flexibilité.
De plus, le service REST doit utiliser les objets de l'API publique (package com.github.funreco)
l'API ne doit pas utiliser le modèle de données mis en place au ticket 16 ?
Non, au ticket 16 c'est le modèle de persistance, qui va servir au moteur, derrière la facade.
Est ce que le schéma suivant vous permet de mieux visualiser ce que nous construisons : https://github.com/dbourdette/fun-reco/wiki/schéma
Voila un premier test avec la lib Jackson, cela à l'air de bien fonctionner, les problèmes viennent maintenant de l'encodage des objets complexes (type Hash) en JSON
Je ne suis pas sûre de bien comprendre le modèle d'utilisation des données : il y a une différence entre le modèle de l'API publique et le modèle de données que nous avons choisi dans fun.reco : comment faire la liaison ?
Vous devez pouvoir supprimer la config JSON du BootStrap.groovy.
Oui il y aura une différence :
C'est la facade qui devra faire la transcription. Cela permettra de faire évoluer le moteur, changer complètement le modèle privé si besoin sans que l'API publique ne bouge. C'est ce que j'ai fait pour mon propre moteur, la partie publique est la même et pourtant les données sont stoquées dans un format très différent de celui que vous avez commencé à faire.
Cela devrait être plus clair après le ticket #17
La config JSON du Bootstrap.groovy permet de paramétrer le encodeAsJson de Groovy pour qu'il ne mettre pas de clés superflues, j'ai essayé sans et le mapping Jackson ne fonctionnait pas
En utilisant le modèle publique, il ne devrait pas (ou presque) y avoir de clés superflues. A la fin, le mapping avec Jackson doit pouvoir fonctionner mais la configuration demandera sans doute aussi quelques lignes. Pour le moment, rien de tout cela n'est très grave.
J'ai quelques questions concernant le schema (et donc la manière d'implémenter l'API REST) :
D'accord, donc le ActionRestController que j'ai commencé n'utilise pas les bons objets c'est bien ça ?
toutafé
Pour vérifier dans les tests que les objets POST dans les webservices sont bien sauvegardés en base, je dois passer par la Facade également (pour compter les objets en base) ? Parce que là par exemple je crois que j'ai une erreur qui vient d'une différence entre le modèle de l'API publique et le modèle de données MongoDB que nous avons choisi (au niveau de l'attribut friendsIds de Profile)
Vu ce qu'on veut tester, je déplacerais plutôt le test dans unit et je ferais un mock de la facade. Ainsi, je vérifie que le service REST appelle bien les bonnes méthodes de la Facade et retourne les bons codes http. Le test de la facade et de la persitance est plus lié au ticket #17
Ainsi, c'est plus simple a tester. Bon par contre, il faut faire un mock
Si vous ne souhaitez pas faire ça, il vaut mieux attendre que la Facade soit opérationnelle via #17
Sinon, quelle est votre erreur ? Je peux au moins essayer de vous indiquer le sens de celle ci.
L'erreur est
Unrecognized field "friendsIds" (Class com.github.funreco.Profile), not marked as ignorable at [Source: java.io.StringReader@7ea47c7a; line: 1, column: 76](through reference chain: com.github.funreco.Profile["friendsIds"])
Je pense qu'elle vient du fait que j'instancie une Action du package fun.reco alors que le controller utilise pour l'instant une implémentation de la facade avec les Action de l'API publique
Unrecognized field vient de jackson qui n'arrive pas a lire un champs dans le modèle.
Depuis votre test, vous utilisez des fun.reco (venant de TestData qui a servi pour la persitance) alors qu'a la désérialisation, le controller s'attend a avoir des com.github.funreco
Bref, dans ActionRESTTests, Action doit être du package com.github.funreco si vous voulez faire un encodeAsJSON(). Et encore, cette méthode doit introduire un attribut class... le mieux serait d'utiliser jackson.
Il faut reconnaitre que notre choix de nom de packages est loin d'être judicieux pour le moment. Nous essayerons de corriger cela prochainement.
Pour les dépendances, le mieux est de faire des mocks afin de pouvoir avancer facilement sans attendre la fin de la Facade.
J'ai fait un premier essai de mock qui n'est pas très concluant, j'ai l'impression que ma facade mock ne n'instancie pas, car je récupère une erreur
Cannot invoke method updateProfile() on null object
or le params.profile est bien récupéré et désérialisé, j'ai fait des tests. Pourriez-vous m'indiquer si j'ai mal configuré le mock ?
grails test-app unit: ProfileRestTests
java.lang.NullPointerException: Cannot invoke method updateProfile() on null object
at com.github.dbourdette.api.ProfileRestController.save(ProfileRestController.groovy:19)
at com.github.dbourdette.api.ProfileRestTests.testSave(ProfileRestTests.groovy:17)
Donc ProfileRestController.groovy ligne 19
if (recommendationFacade.updateProfile(profile)) {
Donc recommendationFacade est null
En fait, en lisant la doc, si on fait un mockFor (au lieu d'un @Mock), il faut tout faire à la main. Donc il faut aussi injecter la dépendance à la main
controller.recommendationFacade = facade.createMock()
Au passage, le code pour envoyer du json dans le body (content) de la requête devrait être le suivant.
Profile profile = new Profile(facebookId: "testFB", email: "testEmail", name: "testName")
controller.request.contentType = "application/json"
controller.request.content = new ObjectMapper().writeValueAsBytes(profile)
Coté serveur, le json est disponible via request.JSON
La doc grails sur les services REST est loin d'être claire.
Effectivement en injectant la dépendance à la main, la facade est bien déclarée, merci.
En revanche, j'ai essayé de passer le JSON via request.content et de le traiter avec request.JSON mais je n'ai pas réussi à le faire marcher. La méthode en passant par params était celle présentée dans la doc du ticket 19
Il faut pouvoir éditer les profils, amis et actions.
Voici les méthodes et les chemins possibles
ProfileRestController
GET /api/profile/{facebookId} affiche un profil POST /api/profile/{facebookId} met a jour un profil
ActionRestController
PUT /api/profile/{facebookId}/actions ajoute une action pour le profil
FriendsRestController
GET /api/profile/{facebookId}/friends affiche les amis d'un profil POST /api/profile/{facebookId}/friends met à jour les amis d'un profil
La démarche est la suivante :
Il faut donc écrire les tests unitaires et les controllers qui vont avec.
J'avais fait un test rapide avec request.content
et cela tournait.
C'est assez mal documenté mais coté client, le contenu doit être envoyé dans le body.
De ce que je comprends, resource dans les mappings (anciennement parseRequest) va lire le body et faire comme si c'était des params.
Ce n'est pas très important, nous pourrons corriger en temps utiles.
Cela semble pas mal, c'est beaucoup plus agréable à lire.
Voici quelques propositions que j'ai comité dans ActionRestControllerTests
Maintenant, on verra comment ces services évolueront à l'usage.
A la suite de #19 (Service REST de test), construire un ensemble de services REST afin d'éditer les profils, amis et actions.
Il faut commencer par définir les chemins pour ces ressources.
Les controleurs doivent faire appel à la facade basique construite en #17 afin de sauver et lire les données en base.
Si nécessaire, on pourra modifier ou rajouter des méthodes à l'interface RecommendationFacade