Comment implémenter facilement notre API ? La réponse ici !
Avant de commencer à implémenter notre API, vous trouverez son code source sur GitHub. Vous pouvez le clonner comme ceci :
$ git clone git@github.com:bref/bref-api.git $ cd bref-api $ git checkout stable
Le repository de notre api est prévu pour être utilisé en tant que submodule et c'est que nous faisons dans notre propre Zia. Par exemple nous voulons l'utiliser dans le dossier ./src/api, par rapport à la racine de notre repository :
$ git submodule add git@github.com:bref/bref-api.git src/api $ cd src/api $ git checkout stable $ cd ../.. $ git add src/api $ git commit -m "Add bref API"
Le logger est représenté sous 2 formes :
C'est une interface classique pour un logger.
On peut mettre un niveau de log général au logger et
logger des messages d'une certaine sévérité grâce à la
méthode log(severity, msg)
.
Seul les messages d'une sévérité supérieur ou égale à celle du logger seront réellement logger.
void example(ILogger *logger) { // On dit au logger de ne logger qu'à partir d'une sévérité // de type ILogger::Error ou supérieur logger->setSeverity(ILogger::Error); // Debug étant un niveau inférieur à Error, le logger va l'ignorer logger->log(ILogger::Debug, "un message de debug"); // C'est une erreur, le logger log le message logger->log(ILogger::Error, "un message d'erreur"); }
Il vous reste donc qu'à implémenter votre classe de log héritant de l'interface ILogger.
Les macros de log sont construites sur le modèle de :
#define LOG(logger, sev) bref::ScopedLogger(logger, sev).log() #define LOG_DEBUG(logger) LOG(logger, bref::ILogger::Debug)
En réalité, afin de réduire l'utilisation de ressources en log ignoré, la macro LOG teste le niveau de log avant de construire un ScopedLogger et de l'utilisation de streams.
La classe ScopedLogger est déjà implémentée, sont utlisation couplée au macros est très simple, même transparente.
void myHandler(ILogger *logger) { logger->setSeverity(ILogger::Warning); // Grâce à la macro aucune stringstream n'est crée ici LOG_DEBUG(logger) << "Un message de debug"; // Ici la stringstream est crée et le message loggé LOG_WARN(logger) << "Un message de warning"; }
La méthode log de votre future classe de log sera appelée à la destruction du ScopedLogger, avec en paramètre le niveau de log fourni ainsi que le message à logger.
Nous somme partis du fait qu'il nous fallait un conteneur pour notre configuration YAML, nous avons donc créé celui-ci en pensant à l'arborescence de celle-ci,
c'est pourquoi celui-ci intègre à la fois deux types : BrefValueList et BrefValueArray, respectivement une std::list
et une std::map
de BrefValue. Au final ce conteneur est
Notre conteneur générique permet de stocker les types suivants :
std::string
;bool
;int
;BrefValueList
: correspondant à une std::list<bref::BrefValue>
;BrefValueArray
: correspondant à une std::map<std::string, bref::BrefValue>
.Pour l'implémenter, rien de plus simple, développez :
void setNull()
;BrefValueList
: void push(const bref::BrefValue &node)
;operator[]
pour le BrefValueArray
;bool isNull() const
;bool asBool() const
.Avoir un conteneur générique, c'est bien, mais implémenter des méthodes pour le parcourir à chaque fois qu'on en a besoin, ça l'est moins.
C'est pourquoi nous avons pensé inclure des méthodes d'aide au parcours de nos BrefValue
pour le corps du serveur, mais aussi les modules.
Pour mieux comprendre, voici un exemple simple : vous voulez récupérer une variable DocumentRoot
dans votre configuration.
Nous avons, ici un extrait de configuration (et oui, en YAML, parcequ'on aime ça) :
DocumentRoot: /var/www VirtualHosts: - ServerName: example.com DocumentRoot: /home/vhosts/example.com - ServerName: example.fr DocumentRoot: /home/vhosts/example.fr - ServerName: example.net
En fonction du VirtualHost où que l'on utilise, la racine de celui-ci n'est pas la même, il nous faut donc des méthodes pour récupérer la valeur la plus pertinente, par exemple,
le DocumentRoot
du VitualHost pour l'host example.com (/home/vhosts/example.com) ou le "général" pour l'host example.net (/var/www).
Cherchant soit une variable de configuration générique, soit une variablde configuration dépendant du contexte d'une requête HTTP, nous avons donc deux fonctions dans notre interface IConfHelper :
virtual const BrefValue & findValue(std::string const & key) = 0; virtual const BrefValue & findValue(std::string const & key, HttpRequest const & request) = 0;
Nous pouvons par exemple récupérer les deux DocumentRoot de l'exemple ci-contre : notre fonction findValue
utilisera le header HTTP Host de la
requête et la variable de configuration ServerName afin de déterminer quel DocumentRoot retourner.
Ayant un BrefValue dans notre API, nous avons tout simplement créer deux classes, HttpRequest et HttpResponse, héritant toutes les deux d'une map de BrefValue, et incluant des getters et setters spécifiques à une requête et une réponse HTTP.