Il y a 3 années · 18 minutes · Cloud

Google App Engine Cloud Endpoint – créer notre api v2 et l’utiliser avec Angularjs

AppEngine_500px
Dans le premier article nous avons mis en place la v1 de notre api todo, déployée en local pour la tester puis déployée sur les serveurs GAE. Pour se concentrer sur l’essentiel nous ne stockions aucun todo et notre api ne retournait qu’une liste statique de todos. Dans cet article nous allons mettre en oeuvre la v2 de l’api, stocker nos todos dans le datastore de GAE via la librairie Objectify, puis hacker TodoMVC Angularjs pour utiliser notre api.

Créer et déployer l’api v2

Pour accéder au datastore nous allons nous appuyer sur la librairie Objectify (https://code.google.com/p/objectify-appengine/wiki/Introduction). Il est également possible d’utiliser JPA ou JDO, toutefois Objectify permet d’offrir une solution intermédiaire entre l’api bas niveau du datastore et les api JPA ou JDO. L’objectif de l’article gardera une utilisation simple de Objectify notre but étant la mise en place d’une api avec Cloud Endpoint.

Récupération du projet du premier article

Avant tout, si vous n’avez pas développé la v1 de l’api de todos du premier article vous pouvez cloner la branche « step3-simple-java-client-api » qui permet de partir exactement là où nous avions laissé le code:

git clone -b step3-simple-java-client-api  https://github.com/jbaptisteclaramonte/gae-cloud-endpoint-todos.gi

Setup d’Objectify

Tout d’abord ajoutons la dépendance vers Objectify. Dans le fichier pom.xml :

<dependency>
 <groupId>com.googlecode.objectify</groupId>
 <artifactId>objectify</artifactId>
 <version>5.0.2</version>
</dependency>
<dependency>
 <groupId>com.google.guava</groupId>
 <artifactId>guava</artifactId>
 <version>14.0.1</version>
</dependency>

Puis dans le fichier web.xml ajouter :

<filter>
 <filter-name>ObjectifyFilter</filter-name>
 <filter-class>com.googlecode.objectify.ObjectifyFilter</filter-class>
</filter>
<filter-mapping>
 <filter-name>ObjectifyFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

Créer le modèle de données pour les Todo

Créer une nouvelle classe Todo.java dans le package fr.xebia.gae.todo.api.v2.model :

package fr.xebia.gae.todo.api.v2.model;

import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;

@Entity
public class Todo {

 @Id
 private Long id; // @Id sur Long => si null un identifiant unique sera auto généré lors d'une insertion
 private String title;
 private boolean completed;

 // nécessaire pour la conversion automatique JSON
 public Todo() {
 }

 public Todo(String title, boolean completed) {
  this.title = title;
  this.completed = completed;
 }

 public Todo(Long id, String title, boolean completed) {
  this.id = id;
  this.title = title;
  this.completed = completed;
 }

 ajouter les getters/setters ici

}

Comme vous pouvez le voir nous positionnons l’annotation @Entity pour que celle-ci puisse être prise en compte par Objectify.

@Id est obligatoire car elle permet de déterminer l’identifiant de notre modèle de données. En typant notre attribut de type Long nous nous assurons que sa valeur sera automatiquement fournit par le Datastore lors de la persistence.

Nous allons ensuite créer une classe TodoRepository.java dans le package fr.xebia.gae.todo.api.v2 qui nous permettra de faire du CRUD sur nos todos :

package fr.xebia.gae.todo.api.v2.repository;
import com.google.appengine.api.datastore.ReadPolicy;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.ObjectifyService;
import fr.xebia.gae.todo.api.v2.model.Todo;

import java.util.Collection;
import java.util.List;

import static com.googlecode.objectify.ObjectifyService.ofy;

public class TodoRepository {

 private static TodoRepository todoRepository = null;

 static {
  ObjectifyService.register(Todo.class);
 }

 private TodoRepository() {
 }

 public static synchronized TodoRepository getInstance() {
  if (null == todoRepository) {
   todoRepository = new TodoRepository();
  }
  return todoRepository;
 }

 public Collection<Todo> findTodos() {
  List<Todo> todos = ofy().load().type(Todo.class).list();
  return todos;
 }

 public Todo create(Todo todo) {
  ofy().save().entity(todo).now();
  return todo;
 }

 public Todo update(Todo editedTodo) {
  if (editedTodo.getId() == null) {
   return null;
  }

  Todo todo = ofy().load().key(Key.create(Todo.class, editedTodo.getId())).now();
  todo.setCompleted(editedTodo.isCompleted());
  todo.setTitle(editedTodo.getTitle());
  ofy().save().entity(todo).now();

  return todo;
 }

 public void remove(Long id) {
  if (id == null) {
   return;
  }
  ofy().delete().type(Todo.class).id(id).now();
 }
}

Les appels à ObjectifyService.register(Class<?> clazz) permettent de donner à Objectify les entités qui seront sous sa responsabilité. Nous effectuons cet appel dans un bloc static de la classe TodoRepository afin de s’assurer que la Todo.class sera bien connu d’Objectify.

Pour utiliser Objectify il faut récupérer son instance avec la méthode statique Objecify.ofy(). Comme nous avons mis Objectify dans l’import static nous appelons simplement ofy().

Puis nous créons une nouvelle classe TodoEndpoint.java qui réprésentera notre api v2:

package fr.xebia.gae.todo.api.v2;

import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.Named;
import fr.xebia.gae.todo.api.v2.model.Todo;
import fr.xebia.gae.todo.api.v2.repository.TodoRepository;

import java.util.Collection;

@Api(
 name = "todos",
 version = "v2"
)
public class TodoEndPoint {

 @ApiMethod(name = "list", httpMethod = ApiMethod.HttpMethod.GET, path="list")
 public Collection<Todo> getTodos() {
  return TodoRepository.getInstance().findTodos();
 }

 @ApiMethod(name = "create", httpMethod = ApiMethod.HttpMethod.POST, path="create")
 public Todo create(Todo todo) {
  return TodoRepository.getInstance().create(todo);
 }

 @ApiMethod(name = "update", httpMethod = ApiMethod.HttpMethod.PUT, path="update")
 public Todo update(Todo editedTodo) {
  return TodoRepository.getInstance().update(editedTodo);
 }

 @ApiMethod(name = "remove", httpMethod = ApiMethod.HttpMethod.DELETE, path="remove")
 public void remove(@Named("id") Long id) {
  TodoRepository.getInstance().remove(id);
 }
}

Testons en local

Dans la fenêtre Terminal, taper:

mvn appengine:devserver

Puis pour ouvrir l’ihm d’exploration de l’api:

 http://localhost:8080/_ah/api/explorer

Maintenant on déploie !

Hé ! Mais attendez ! Avant de déployer, on va incrémenter notre numéro de version pour l’application : dans le fichier appengine-web.xml modifier la valeur de l’élément <version>

<version>v2-0</version>

C’est bon maintenant on peut déployer :

mvn appengine:update

Pour l’url de votre application, souvenez vous il faut utiliser le format suivant:

http://<Application identifier>.appspot.com

Pour moi l’Application identifier est todo-endpoint l’url d’accès est donc http://todo-endpoint.appspot.com et comme je veux allez sur l’ihm d’exploration de mon api je tape:

http://todo-endpoint.appspot.com/_ah/api/explorer

Appeler notre api à partir d’un client java généré

Le premier article avait présenté comment faire générer votre client Java pour votre api. Je vous propose de recommencer mais cette fois nous allons pointer notre sur le serveur GAE.

Première étape générer notre client java :

mvn clean compile appengine:endpoints_get_client_lib

Cette étape génère un projet maven dans le répertoire target/endpoints-client-libs puis lance un mvn install dans le projet généré permettant d’avoir dans votre repository maven local l’artifact du client de l’api.

Il est intéressant de regarder les logs de maven pour comprendre un peu ce qu’il se passe lors de la génération de la lib cliente:

[INFO] ------------------------------------------------------------------------
[INFO] Building todo-api 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...
[INFO] Google App Engine Java SDK - get endpoints discovery doc...
[INFO] Using Class Name:fr.xebia.gae.todo.api.v1.TodoEndPoint
[INFO] Using Class Name:fr.xebia.gae.todo.api.v2.TodoEndPoint
...
[INFO] --- appengine-maven-plugin:1.9.4:endpoints_get_client_lib (default-cli) @ todo-api ---
[INFO]
[INFO] Google App Engine Java SDK - Generate endpoints get client lib
[INFO] Using Class Name:fr.xebia.gae.todo.api.v1.TodoEndPoint
[INFO] Using Class Name:fr.xebia.gae.todo.api.v2.TodoEndPoint
...
API client library written to .../gae-cloud-endpoint-todos/todo-api/target/generated-sources/appengine-endpoints/WEB-INF/todo-v1-java.zip
API client library written to .../gae-cloud-endpoint-todos/todo-api/target/generated-sources/appengine-endpoints/WEB-INF/todos-v2-java.zip
[INFO] BUILDING Endpoints Client Library from: .../gae-cloud-endpoint-todos/todo-api/target/endpoints-client-libs/todo/pom.xml
...
[INFO] ------------------------------------------------------------------------
[INFO] Building todo v1-1.18.0-rc-SNAPSHOT v1-1.18.0-rc-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...
[INFO] ------------------------------------------------------------------------
[INFO] Building todos v2-1.18.0-rc-SNAPSHOT v2-1.18.0-rc-SNAPSHOT
[INFO] ------------------------------------------------------------------------
...

Lors de la première partie le code est compilé, puis le goal endpoints_get_discovery_doc est appelé, il permet de générer les fichiers *.discovery nécessaires au fonctionnement du framework Endpoint.

Enfin le goal endpoints_get_client_lib est appelé, dans notre cas il génère deux fichiers todo-v1-java.zip et todo-v2-java.zip, respectivement pour la v1 et la v2 de notre api.

S’en suit alors le build puis l’installation dans le repo local des versions v1-1.18.0-rc-SNAPSHOT v2-1.18.0-rc-SNAPSHOT de notre api.

La convention de nommage des librairies de l’api est décomposée en trois parties. La première partie (v1 ou v2 ici) correspond donc à la version de notre api, la seconde partie (1.18.0-rc) est liée à la version de la lib google-api-client dont nous dépendrons dans notre application cliente, enfin pour la partie SNAPSHOT … et bien je n’ai pas actuellement trouvé les conditions permettant de l’éviter.

Nous réutilisons le projet todo-api-client créé lors de l’article précédent pour la v1 de l’api. Lors de l’explication précédente nous avons pu voir que si nous voulions utiliser la v2 de notre api nous allons devoir changer la version de notre dépendance vers la lib du client de l’api.

Concrètement la dépendance devient v2-1.18.0-rc-SNAPSHOT:

<dependency>
 <groupId>com.appspot.todo_endpoint</groupId>
 <artifactId>todos</artifactId>
 <version>v2-1.18.0-rc-SNAPSHOT</version>
</dependency>

Voici un exemple pour créer deux todos, les lister et les supprimer dans la foulé:

package fr.xebia.gae.todo.api;
import com.appspot.todo_endpoint.todos.Todos;
import com.appspot.todo_endpoint.todos.model.*;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import java.io.IOException;
public class App
{
    public static void main( String[] args ) throws IOException {
        HttpTransport httpTransport = new NetHttpTransport();
        JsonFactory jsonFactory = new JacksonFactory();
        Todos.Builder builder = new Todos.Builder(httpTransport, jsonFactory, null);
        builder.setRootUrl("https://todo-endpoint.appspot.com/_ah/api");
        Todos todosApi = builder.build();
        Todo newTodo = new Todo();
        newTodo.setTitle("hello");
        todosApi.create(newTodo).execute();
        newTodo = new Todo();
        newTodo.setTitle("world");
        todosApi.create(newTodo).execute();
        TodoCollection todoCollection = todosApi.list().execute();
        for (Todo todo : todoCollection.getItems()) {
            System.out.println(todo.getTitle() + "(" + todo.getId() + ")");
            if (todo.getTitle().equals("hello")) {
                todosApi.remove(todo.getId());
                System.out.println("=> deleted");
            }
            if (todo.getTitle().equals("world")) {
                todosApi.remove(todo.getId()).execute();
                System.out.println("=> deleted");
            }
        }
    }
}

Hacker TodoMVC AngularJS pour appeler notre api

Développer une api c’est cool mais l’utiliser c’est encore plus cool. Dans la suite de l’article nous allons hacker l’application TodoMVC AngularJS puis y intégrer les appels à notre api.

Etape 1 : Récupérer le code de TodoMVC pour AngularJS

La première étape est de récupérer l’application TodoMVC pour AngularJS. Pour cela nous clonons le repository présent sur github:

git clone https://github.com/tastejs/todomvc.git

Tout ne nous intéresse pas, nous n’allons prendre que la partie AngularJS, copiez le contenu de ./todomvc/architecture-examples/angularjs dans votre projet java endpoint, ./todo-api/main/webapp

On se retrouve donc avec la partie webapp de notre projet comme suit:

├── todo-api
│   ├── pom.xml
│   ├── src
│   │   └── main
│   │       ├── java
│   │       │   └── fr ...
│   │       └── webapp
│   │           ├── WEB-INF
│   │           ├── bower.json
│   │           ├── bower_components
│   │           ├── index.html
│   │           ├── js
│   │           ├── readme.md
│   │           └── test

Nous pouvons dès maintenant vérifier que l’application TodoMVC fonctionne en standalone:

mvn appengine:devserver

On ouvre le browser sur http://localhost:8080 et ça devrait fonctionner.

Idem en déployant sur GAE:

mvn appengine:update

Puis allez sur l’url de votre application et l’application TodoMVC fonctionne en standalone.

Etape 2 : charger notre api en js

Maintenant nous allons valider le chargement de notre api dans l’application TodoMVC et pour cela il faut comprendre comment fonctionne les api js avec Endpoint. Intuitivement vous pensez probablement que l’on va générer un fichier js qui contiendra le proxy pour appeler notre api sur le serveur Endpoint … et bien non pas de fichier généré au préalable !!

Mais alors comment cela peut-il fonctionner ?

Et bien Google propose une librairie js qui va créer un proxy dynamique de votre api : https://apis.google.com/js/client.js

Voici un exemple d’utilisation minimal pour charger puis utiliser notre api todos :

<!-- Tout à la fin de la page html: -->
<script>
    var init = function() {
  var rootApi = 'http://localhost:8080/_ah/api';
  gapi.client.load('todos', 'v2', function() {
      console.log("todos api loaded");
  }, rootApi);
  // Appeler l'api todos
  gapi.client.todos.list().execute(function(resp) {
            console.log(resp);
        });
    }
</script>
<script src="https://apis.google.com/js/client.js?onload=init"></script> 

Dans le cadre d’une intégration avec AngularJS, Google conseille de positionner le script client.js à la fin du chargement de la page afin de s’assurer qu’Angular a correctement terminé l’initialisation de l’application.

Cela peut potentiellement suffire, mais idéalement nous aimerions nous intégrer un peu plus avec Angularjs que nous utilisons dans ce TodoMVC.

Pour cela nous allons ajouter au controller todoCtrl.js de l’application une référence vers l’objet window du browser, et nous allons transmettre l’init à l’objet window, nous permettant de nous retrouver dans AngularJS pour l’initialisation du proxy dynamique vers l’api todos.

Nous allons donc effectuer les modifications suivantes dans les fichiers de TodoMVC:

Dans index.html:

  <script src="js/directives/todoEscape.js"></script>
  <!-- ------ début du code ajouté ------ -->
        <script>
            var init = function() {
                // sera intercepté par angularjs
                window.init();
            }
        </script>
        <script src="https://apis.google.com/js/client.js?onload=init"></script>
  <!-- ------ fin du code ajouté ------  -->
 </body>
</html>

Dans todoCtrl.js:

// ATTENTION : copiez le code ajouté et n'oubliez pas d'ajouter $window dans les éléments injectés au contrôleur ci dessous
angular.module('todomvc')
 .controller('TodoCtrl', function TodoCtrl($scope, $routeParams, $window, $filter, todoStorage) {
  'use strict';
  var todos = $scope.todos = todoStorage.get();
  $scope.newTodo = '';
  $scope.editedTodo = null;
 
  // ------ début du code ajouté ------
        /**
         * Ajout pour fonctionner avec Google Cloud Endpoint
         * Fonction interceptant l'appel à window.init() effectué dans index.html
         */
        $window.init= function() {
            console.log("$window.init called");
            $scope.$apply($scope.load_gapi_todo_lib);
        };
        /**
         * Charge l'api todos
         */
        $scope.load_gapi_todo_lib = function() {
            console.log("load_todo_lib called");
            var rootApi = 'http://localhost:8080/_ah/api';
            gapi.client.load('todos', 'v2', function() {
                console.log("todos api loaded");
                gapi.client.todos.list().execute(function(resp) {
     console.log("todos api sucessfully called");
                    console.log(resp);
                });
            }, rootApi);
        };
 
  // ------ fin du code ajouté ------
  $scope.$watch('todos', function (newValue, oldValue) {

J’attire votre attention à ne pas oublier d’ajouter $window dans la liste des éléments qui seront injectés au contrôleur !

Ensuite nous pouvons redémarrer notre application en local et vérifier dans la console de notre browser préféré que tout s’est correctement déroulé en ayant les logs suivantes:

ScreenShot-log-api-loaded

Etape 3 : Intégrer les appels à notre api

Dans l’étape 2 nous avons validé la méthode d’intégration d’AngularJS avec notre api. Nous allons continuer l’intégration pour avoir l’application TodoMVC fonctionnelle.

Nous revenons sur js/controllers/todoCtrl.js et nous y copions le contenu suivant :

/*global angular */
/**
 * The main controller for the app. The controller:
 * - retrieves and persists the model via the todoStorage service
 * - exposes the model to the template and provides event handlers
 */
angular.module('todomvc')
 .controller('TodoCtrl', function TodoCtrl($scope, $routeParams, $window, $filter, todoStorage) {
  'use strict';
        $scope.todos = [];
        $scope.loadTodos = function() {
            todoStorage.list(function(resp) {
                console.log(resp);
                console.log("todos api sucessfully called");
                if (resp.items != undefined) {
                    $scope.todos = resp.items;
                }
                $scope.$apply();
            });
        };
        $scope.loadTodos();
  $scope.newTodo = '';
  $scope.editedTodo = null;
        /**
         * Ajout pour fonctionner avec Google Cloud Endpoint
         * Fonction interceptant l'appel à window.init() effectué dans index.html
         */
        $window.init= function() {
            console.log("$window.init called");
            $scope.$apply($scope.load_gapi_todo_lib);
        };
        /**
         * Charge l'api todos
         */
        $scope.load_gapi_todo_lib = function() {
            console.log("load_todo_lib called");
            var rootApi = $window.location.origin + '/_ah/api';
            gapi.client.load('todos', 'v2', function() {
                console.log("todos api loaded");
    // le flag isBackEndReady va nous permettre de ne pas faire d'appel 
    // si l'api todos n'est pas chargée
                todoStorage.isBackEndReady = true;
                $scope.loadTodos();
            }, rootApi);
        };

  $scope.$watch('todos', function (newValue, oldValue) {
   $scope.remainingCount = $filter('filter')($scope.todos, { completed: false }).length;
   $scope.completedCount = $scope.todos.length - $scope.remainingCount;
   $scope.allChecked = !$scope.remainingCount;
  }, true);

  // Monitor the current route for changes and adjust the filter accordingly.
  $scope.$on('$routeChangeSuccess', function () {
   var status = $scope.status = $routeParams.status || '';
   $scope.statusFilter = (status === 'active') ?
    { completed: false } : (status === 'completed') ?
    { completed: true } : null;
  });
        $scope.addTodo = function () {
            var newTodoTile = $scope.newTodo.trim();
            if (!newTodoTile.length) {
                return;
            }
            var newTodo = {
                title: newTodoTile,
                completed: false
            };
            todoStorage.create(newTodo, function(todoResp) {
                $scope.todos.push({
                    id: todoResp.id,
                    title: todoResp.title,
                    completed: todoResp.completed
                });
                $scope.$apply();
            });
            $scope.newTodo = '';
        };
  $scope.editTodo = function (todo) {
   $scope.editedTodo = todo;
   // Clone the original todo to restore it on demand.
   $scope.originalTodo = angular.extend({}, todo);
  };
  $scope.doneEditing = function (todo) {
            $scope.editedTodo = null;
            todo.title = todo.title.trim();
            if (!todo.title) {
                $scope.removeTodo(todo);
            } else {
                todoStorage.update(todo, function(todo) {
                    console.log('todo with id ' + todo.result.id + ' successfully updated');
                });
            }
  };
  $scope.revertEditing = function (todo) {
   $scope.todos[$scope.todos.indexOf(todo)] = $scope.originalTodo;
   $scope.doneEditing($scope.originalTodo);
  };
        $scope.completeTodo = function (todo) {
            todo.completed = !todo.completed;
            todoStorage.update(todo, function() {
                $scope.$apply();
            })
        };
  $scope.removeTodo = function (todo) {
            $scope.todos.splice($scope.todos.indexOf(todo), 1);
            todoStorage.remove(todo, function() {
                $scope.$apply();
            })
  };
  $scope.clearCompletedTodos = function () {
   $scope.todos = $scope.todos.filter(function (val) {
    return !val.completed;
   });
  };
  $scope.markAll = function (completed) {
            $scope.todos.forEach(function (todo) {
    todo.completed = !completed;
   });
  };
 });

La majeur partie du code est celui d’origine, les principales différences sont dans les appels systématiques au service AngularJS todoStorage qui contient concrètement notre code d’accès à notre api.

On remarquera que nous avons également ajouté un flag isBackEndReady permettant de protéger les appels à l’api tant que celle-ci n’aurait pas été correctement chargée.

Maintenant passons aux modifications à apporter au fichier js/services/todoStorage.js :

/*global angular */
/**
 * Services that persists and retrieves TODOs from localStorage
 */
angular.module('todomvc')
 .factory('todoStorage', function () {
  'use strict';
  var STORAGE_ID = 'todos-angularjs';
  return {
            isBackEndReady: false,
            list: function (callback) {
                if (!this.isBackEndReady) {
                    console.log("todos api is not ready");
                    return;
                }
                console.log("getting todos list");
                gapi.client.todos.list().execute(callback);
            },
            create: function (todo, callback) {
                if (!this.isBackEndReady) {
                    console.log("todos api is not ready");
                    return;
                }
                console.log("create :" + todo);
                gapi.client.todos.create(todo).execute(callback);
            },
            update: function (todo, callback) {
                if (!this.isBackEndReady) {
                    console.log("todos api is not ready");
                    return;
                }
                console.log("update :" + todo);
                gapi.client.todos.update(todo).execute(callback);
            },
            remove: function (todo, callback) {
                if (!this.isBackEndReady) {
                    console.log("todos api is not ready");
                    return;
                }
                console.log("remove :" + todo);
                gapi.client.todos.remove({"id": todo.id}).execute(callback);
            }
  };
 });

Avant d’appeler l’api nous vérifions que le flag isBackEndReady est bien à true, puis nous faisons l’appel.

Dernière petite touche dans le fichier index.html rous allons ajouter la gestion du click sur la coche d’un todo afin de transmettre le nouveau status d’un todo.

Recherchez :

<input class="toggle" type="checkbox" ng-model="todo.completed">

et ajoutez ng-click= »completeTodo(todo) » comme suit:

<input class="toggle" type="checkbox" ng-model="todo.completed" ng-click="completeTodo(todo)">

Tester l’application finale

Nous pouvons lancer notre serveur local

mvn appengine:devserver

et tester le fonctionnement de l’application en allant sur http://localhost:8080/

Pour déployer sur votre application hébergée par GAE

mvn appengine:update

Attention pour fonctionner il vous faudra taper l’url avec https sinon l’api ne se chargera pas correctement.

Conclusion

Nous avons vu comment intégrer les appels à notre api avec AngularJS, cela est assez simple mais attention à bien respecter l’ordre de chargement des librairies.

Au prochain épisode nous allons sécuriser notre api todos pour que seuls nous puissions y accéder, à bientôt !

5 réflexions au sujet de « Google App Engine Cloud Endpoint – créer notre api v2 et l’utiliser avec Angularjs »

  1. Publié par Boisney Philippe, Il y a 3 années

    Bonjour !

    Merci beaucoup pour votre tutoriel (Et qui plus est, en Français, la classe =D). Je cherchais depuis un petit moment un moyen de pouvoir utiliser Objectify avec les Endpoints en utilisant mes propres API, et grâce à ce tuto, j’ai enfin ma réponse ! Cela fait quelques mois que j’apprends Android et les services Googles Cloud Infrastructure (C’est un peu chaud, étant issus du monde du réseau et télécom, le dev objet est plus complexe que je ne le pensais), et votre article m’a beaucoup aidé.

    Félicitation pour cet article de qualité, vous donnez envie de bosser avec vous ! =)

    A bientôt,

  2. Publié par Pyroxe, Il y a 3 années

    Aller vous faire un exemple de sécurité par authentification autre que avec un compte gmail?

  3. Publié par jbclaramonte, Il y a 3 années

    @philippe : merci ^^
    @Pyroxe : l’article qui complètera Endpoint se fera effectivement avec une authentification gmail qui est intégrée avec service Endpoint

  4. Publié par Tom, Il y a 2 années

    Merci pour ces deux articles !
    Mais je suis surpris par le temps de réponse de mes Endpoints, plus de 10 secondes à froid. Google nous dit que c’est normal, il faut d’abords un certain nombre de requêtes pour scaler correctement la demande (Warmup requests). Pas très rassurant quand même.
    J’attend quand même le 3e article :)

  5. Publié par Maxime Pauvert, Il y a 2 années

    J’ai développé un module angular pour encore plus simplifier l’utilisation de google cloud endpoints pour angular. Voici le module : https://github.com/maximepvrt/angular-google-gapi
    N’hésitez pas à faire vos retours

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *