API REST: Node.js

Incluya rápidamente operaciones CRUD, manejo de errores, clasificación y filtrado en su API REST hoy

Configuración del entorno

¡Comencemos! Primero, déjame revisemos las herramientas que usaremos:

  • Node.js: Un entorno de tiempo de ejecución JavaScript de código abierto que le permite ejecutar código fuera de un navegador. Desarrollaremos nuestra API RESTful en JavaScript en un servidor Node.js
  • MongoDB: la base de datos en la que escribiremos nuestros datos.
  • Postman: la herramienta que usaremos para probar nuestra AP.
  • VSCode: puedes usar el editor de texto que quieras. Para el ejercicio usaremos VSCode.

Si no tiene ninguna de estas herramientas configuradas, vaya a Configurando Node.js y MongoDB Atlas que lo guía paso a paso.

Pero, antes de empezar, revisemos algunos conceptos:

Introducción a REST API

Hoy en día se escucha sobre REST API en todas partes en el mundo tecnológico actual, pero ¿qué es? Para empezar, API significa Interfaz de Programación de Aplicaciones.

¿Cuál es el beneficio de una API? Permite que dos piezas de software se comuniquen entre sí. Existen muchos tipos de API: SOAP, XML-RPC, JSON-RPC, pero en este artículo hablaremos de REST.

¿Qué es REST? Es sinónimo de REpresentational State Transfer (Transferencia de Estado de Representación). Es un estilo de arquitectura de software que se utiliza para crear servicios web. REST ha facilitado que los sistemas informáticos se comuniquen entre sí a través de Internet.

¿Como funciona? Bastante similar a cómo escribe «rest api» en Google y se devuelven los resultados de búsqueda. Explicándolo de la forma más sencilla, un cliente realiza una llamada (solicitud) en forma de URL a un servidor que solicita algunos datos. A continuación se muestra un ejemplo de una búsqueda en Google para «API REST»

www.google.com/search?q=rest+api

El servidor luego responde con los datos (respuesta) a través del protocolo HTTP. Estos datos aparecen en notación JSON y se convierten en una imagen estéticamente agradable en su navegador: los resultados de búsqueda de Google.

En resumen, envía una solicitud en forma de URL y recibe una respuesta en forma de datos.

¿Cómo se reciben estos datos normalmente? Generalmente en formato JSON. JSON (Javascript Object Notation) describe sus datos en pares clave-valor. JSON facilita la lectura de los datos tanto en máquinas como en humanos.

Requests

Primero, es importante comprender que las solicitudes constan de cuatro partes.

Endpoint: la URL que solicita. Típicamente compuesto por una raíz y una ruta. P.ej. https://www.google.com/search?q=rest+api, donde la raíz es https://www.google.com y la ruta es / search? q = rest + api.
Método: el tipo de solicitud que envía al servidor o «verbo HTTP». Así es como podemos ejecutar nuestras operaciones CRUD (Crear, Leer, Actualizar o Eliminar). Los cinco tipos son GET, POST, PUT, PATCH y DELETE. (fuente: https://developer.mozilla.org/es/docs/Web/HTTP/Methods)
Encabezados: información adicional que se puede enviar al cliente o servidor que se utiliza para ayudar a sus datos de alguna manera. Por ejemplo, se puede usar para autenticar a un usuario para que puedan ver los datos. O puede decirle cómo se deben recibir los datos (aplicación / JSON).
Datos (body): la información que queremos recibir de nuestra solicitud como JSON.

Revisando con mas detalle los endpoints: dada la importancia de estos, estos son los conceptos adicionales que debemos manejar:

  • Dos puntos (:) Se usa para indicar una variable en la cadena. En API REST, verá endpoints como :username (o algo similar). Solo sepa que cuando lo pruebe, debe reemplazar ese nombre de usuario con un nombre de usuario real. Por ejemplo:

prueba.com/users/:username/articles?query=value&query2=value2
prueba.com/users/epadilla/articles?published=true&date=today

  • Interrogación de cierre (?): Comienza los parámetros de consulta. Los parámetros de consulta son conjuntos de pares clave-valor que se pueden usar para modificar sus solicitudes. Aquí hay un ejemplo de un endpoint en el que quiero ver mis artículos y si han sido publicados o no:

prueba.com/users/:username/articles?query=value
prueba.com/users/epadilla/articles?published=true

  • Ampersand (&): Se utiliza para separar los parámetros de consulta cuando desea usar múltiples. Por ejemplo, podemos ver mis artículos publicados y publicados hoy

prueba.com/users/:username/articles?query=value&query2=value2
prueba.com/users/epadilla/articles?published=true&date=today

Una API REST debe tener al menos las siguientes cuarta partes:

  • Servidor: se utiliza para establecer todas las conexiones que necesitamos, así como para definir información importante como puntos finales, puertos y enrutamiento.
  • Modelo: ¿Cómo son nuestros datos?
  • Rutas: ¿A dónde van nuestros endpoint?
  • Controladores: ¿Qué hacen nuestros endpoints?

Configurando el Server:

Empecemos agregando las dependencias:

npm install mongoose

npm install body-parser

npm install express

Después de instalar las dependencias su package.json debe lucir similar al siguiente:

Ahora si, empezamos por el main.js. sustituyamos el código, por el siguiente:

var express = require("express"),
  app = express(),
  port = process.env.PORT || 3000,
  mongoose = require("mongoose"),
  bodyParser = require("body-parser"),
  Entry = require("./api/models/leaderboardModel");
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

mongoose.connect(
  "mongodb+srv://ryangleason:leaderboard@cluster0-6mky3.mongodb.net/RESTTutorial?retryWrites=true&w=majority",
  { useUnifiedTopology: true, useNewUrlParser: true, useFindAndModify: false }
);

var routes = require("./api/routes/leaderboardRoutes");
routes(app);

app.listen(port);

console.log("API server started on " + port);

Explicando un poco este archivo:

  • Express: el marco de la aplicación web para Node.js. Esto inicia un servidor y escucha las conexiones en el puerto 3000.
  • Mongoose: nos ayuda a modelar objetos para MongoDB. Ayuda a gestionar las relaciones entre los datos, valida el esquema y, en general, es útil para comunicarse desde Node.js a MongoDB.
  • bodyParser: es necesario para interpretar las solicitudes. Funciona extrayendo la parte del cuerpo de una solicitud entrante y la expone como req.body y como JSON.
  • Entry: este es el nombre de nuestro esquema. Necesitamos incluir esto en nuestro app.js porque el esquema debe estar «registrado» para cada modelo que usemos.
  • mongoose.connect (cadena uri, {opciones}): establece una conexión con nuestra base de datos Atlas MongoDB. Establecemos estas opciones para eliminar las advertencias de desuso.
  • routes (app): – Define cómo responderán los endpoints de una aplicación a las solicitudes de los clientes. Esto apuntará a las rutas que nos definimos.

Antes de poder probar nuestro REST API, necesitamos crear nuestro modelo, rutas y el controlador. Así es como se ve la estructura de directorios del proyecto:

Creando el modelo

var mongoose = require("mongoose");
var Schema = mongoose.Schema;

var EntrySchema = new Schema({
  player: {
    type: String,
    required: "Please enter a name"
  },
  score: {
    type: Number,
    required: "Please enter a score"
  },
  registered: {
    type: String,
    default: "No"
  }
});

module.exports = mongoose.model("Entry", EntrySchema, "Leaderboard");

Se ha creado un modelo básico, que solo contiene tres campos. Es importante comprender que estamos utilizando un esquema Mongoose. Cada esquema se asigna a una colección MongoDB y define la estructura de los documentos dentro de esa colección. Luego verá que al final estamos exportando nuestro esquema, llamado Entry, y exponiéndolo al resto de nuestra aplicación.
Cada Entry representará un registro diferente en nuestra «tabla» de clasificación donde una Entry es el nombre del jugador, el puntaje y si están registrados.

Definiendo Rutas

module.exports = app => {
  var leaderboard = require("../controllers/leaderboardController");

  app
    .route("/entries")
    .get(leaderboard.read_entries)
    .post(leaderboard.create_entry);

  app
    .route("/entries/:entryId")
    .get(leaderboard.read_entry)
    .put(leaderboard.update_entry)
    .delete(leaderboard.delete_entry);
};

Las rutas definirán lo que sucede cuando un usuario llega a uno de nuestros endpoints. También es cómo determinamos qué operación CRUD se utiliza para interactuar con los datos en nuestra base de datos.

  • POST —Create un entry
  • GET — Read un entry
  • PUT — Update un entry
  • DELETE — Delete un entry

Definamos el Controlador

var mongoose = require("mongoose"),
  Entry = mongoose.model("Entry");

exports.read_entries = async (req, res) => {
  ret = await Entry.find();
  res.json(ret);
};

exports.create_entry = async (req, res) => {
  const new_entry = new Entry(req.query);
  ret = await new_entry.save();
  res.json(ret);
};

exports.read_entry = async (req, res) => {
  ret = await Entry.findById(req.params.entryId);
  res.send(ret);
};

exports.update_entry = async (req, res) => {
  ret = await Entry.findByIdAndUpdate({ _id: req.params.entryId }, req.body, {
    new: true
  });
  res.json(ret);
};

exports.delete_entry = async (req, res) => {
  await Entry.deleteOne({ _id: req.params.entryId });
  res.json({ message: "Entry deleted" });
};

Aquí es donde «sucede la magia». Entonces, ¿qué está sucediendo cuando nuestras rutas se ven afectadas? Este es el mínimo necesario para una API completamente funcional. Cada uno de estos métodos corresponde con una ruta: notará que todos los nombres son los mismos que las rutas.
Cada uno de estos métodos incluye dos parámetros: la solicitud y la respuesta (req, res). Luego usamos el método Mongoose que coincide con la operación que estamos tratando de implementar. Por ejemplo, para una solicitud GET, queremos encontrar todos los documentos y devolverlos como un objeto JSON de respuesta. Para hacerlo, utilizamos el método find().
Lo mismo vale para POST. Queremos crear un registro, por lo que utilizamos el método Mongoose, save(), para escribir ese registro en la base de datos.

Ha llegado el momento de probar nuestra aplicación.

Ahora realice pruebas desde el navegador. también puede usar curl y Postman.

Creemos nuestro primer registro, usando postman

Así es como se verá una solicitud POST enviada a localhost:3000/Entries. Aquí está la respuesta que debe obtener al presionar el botón Send:

¡Felicidades! Acaba de ingresar su primer registro en la base de datos. Puede confirmarlo consultando las Collections en MongoDB Atlas:

Una solicitud GET a localhost:3000/entries recibirá todos los documentos en nuestra base de datos como respuesta.

Una solicitud GET a localhost:3000/entries /: entryId (ejemplo de arriba: localhost:3000/entries/5e5a731b3e8009a0b9bdd9ff) devolverá solo un registro.
Una solicitud PUT enviada a localhost:3000/entries/:entryId actualizará la entrada con lo que haya puesto en el cuerpo.
Una solicitud DELETE enviada a localhost:3000/entries/:entryId eliminará la entrada especificada.

Gestión de Errores

Para el manejo de errores con las API REST, vamos a utilizar declaraciones try-catch. Vamos a «intentar» hacer la solicitud y si eso falla, «atraparemos» el error. Esta es una manera de manejar con gracia los errores. A continuación se muestran los tipos de errores que puede encontrar al crear una solicitud de API:

  • 100 level (informational) — El servidor reconoce una solicitud.
  • 200 level (Successful) — El servidor completó la solicitud.
  • 300 level (Redirect) — El cliente debe hacer más para completar la solicitud.
  • 400 level (Client Error) —El cliente envió una solicitud no válida.
  • 500 level (Server Error) — El servidor no pudo completar la solicitud debido a un error del servidor.

Cambie su controlador con el siguiente código:

var mongoose = require("mongoose"),
  Entry = mongoose.model("Entry");

exports.read_entries = async (req, res) => {
    try {
        const ret = await Entry.find();
        res.json(ret);
    } catch (error) {
    res.send({ message: "Bad request: " + error });
    }
};

exports.create_entry = async (req, res) => {
    try {
        const new_entry = new Entry(req.query);
        ret = await new_entry.save();
        res.json(ret);
    } catch (error) {
        res.send({ message: "Bad request: " + error });
    }
};

exports.read_entry = async (req, res) => {
    try {
        const ret = await Entry.findById(req.params.entryId);
        res.send(ret);
    } catch (error) {
        res.send({ message: "Bad request: " + error });
    }
};

exports.update_entry = async (req, res) => {
    try {
        const ret = await Entry.findByIdAndUpdate(
          { _id: req.params.entryId },
          req.query,
          { new: true }
        );
        res.json(ret);
    } catch (error) {
        res.send({ message: "Bad request: " + error });
    }    
};

exports.delete_entry = async (req, res) => {
    try {
        const ret = await Entry.deleteOne({ _id: req.params.entryId });
        res.json({ message: "Deleted entry" });
    } catch (error) {
        res.send({ message: "Bad Request: " + error });
    }
};

Para realizar la prueba, enviemos una solicitud incorrecta para que podamos ver si nuestro manejo de errores funciona:

Ordenado

Por ejemplo, si queremos ordenar en orden descendente por puntaje. Tomamos el parámetro de clasificación de la cadena de URL, verificamos si es verdadero y luego ejecutamos una clasificación simple.

Solo tenemos que actualizar nuestro método read_entries:

exports.read_entries = async (req, res) => {
  try {
    const ret = await Entry.find();
    if (req.query.sort === "true")
      res.json(ret.sort((a, b) => b.score - a.score));
    else res.json(ret);
  } catch (error) {
    res.send({ message: "Bad Request: " + error });
  }
};

Tomamos la variable sort de nuestra cadena de consulta de URL. La forma en que accedemos a esto es req.query.sort.

Realice la prueba agregando sort=true a nuestro endpoint.

Filtrado

Ahora realizaremos un filtro para mostrar solo las entradas registradas. ¿Cómo hacemos eso? Pues, agregamos un filtro. Nuevamente, por simplicidad, solo vamos a modificar el método read_entries.

exports.read_entries = async (req, res) => {
  try {
    var ret = await Entry.find();
    if (req.query.sort === "true") {
      ret = ret.sort((a, b) => b.score - a.score);
    }
    if (req.query.registered === "yes") {
      ret = ret.filter(a => a.registered === "yes");
    }
    res.json(ret);
  } catch (error) {
    res.send({ message: "Bad GET Request: " + error });
  }
};

Tomamos la variable registered de la URL y, si es igual a «yes«, filtramos todas las entradas que cumplan esta condición.

Para probarlo, agregue registered=yes a su endpoint:

Felicidades. Su primer CRUD API REST está listo.

Summary

  • Creamos nuestro primer servidor REST
  • Realizamos operaciones CRUD
  • Manejamos los errores
  • Ordenamos los datos utilizando parámetros de consulta
  • Filtramos los datos utilizando parámetros de consulta

Configurando Node.js y MongoDB Atlas

Una guía rápida para iniciar un entorno con Node.js y MongoDB

Objetivo: Crear una aplicación Node.js simple después de configurar un entorno de desarrollo local, probar las API REST a través de Postman y establezca una conexión de base de datos a MongoDB Atlas.

Sigue leyendo «Configurando Node.js y MongoDB Atlas»

Valoración de Inventario en tiempo real en Odoo

Para realizar la valoración del inventario en tiempo real se debe considerar lo siguiente:

  • Para cada producto se debe realizar configuraciones apropiadas en
    • Cuentas contables
    • Método de coste
    • Tipo de valoración de inventario
screenshot2

Definiendo las cuentas contables:

  • Cuenta de Inventario: Esta se debe configurar en el campo «Cuenta de valoración de existencias»
  • Cuenta de Costo: Se debe configurar en los campos:
    • Cuenta Costo de Venta
    • Cuenta de entrada de existencias
    • Cuenta de salida de existencias
  • Cuenta de Ingresos: Esta se debe configurar en el campo «Cuenta de ingresos»

Adicionalmente, también puede decidir la estrategia de retirada: FIFO o LIFO

Tal como lo muestra la imágen de la derecha.

En el caso particular de la captura de pantalla usada de muestra, cualquier artículo que se agregue a la categoría Combustibles tendrá asignadas las cuentas contables que se le han configurado; quedando listo para registrar adecuadamente el inventario.
Es importante recordar  que esta misma configuración se puede realizar también producto a producto
screenshot3

Tipo de coste y valoración del inventario

En la tarjeta del artículo, se deben definir estos parámetros:

  • Método de Coste: Abastecimientos /  Método de coste se debe cambiar a Precio medio
  • Valoración del Inventario: Contabilidad / Valoración del inventario se debe cambiar a Tiempo real (automatizado)

Con la configuración anterior, Odoo automáticamente realizará las partidas contables correspondientes cuando se realicen movimientos de entrada y salida de inventario.

wkhtmltopdf on Odoo

1) Download wkhtmltopdf version from wkhtmltopdf.org depend on your system arch (32 or 64 bit)

wget wget http://downloads.sourceforge.net/project/wkhtmltopdf/0.12.1/wkhtmltox-0.12.1_linux-trusty-amd64.deb

2) Install the package using commnad :

sudo dpkg -i wkhtmltox-0.12.1_linux-trusty-amd64.deb

3) Copy binary or wkhtmltopdf to /usr/bin location from ./usr/local/bin use command

sudo cp /usr/local/bin/wkhtmltopdf /usr/bin/

4) Restart Odoo server and try once again.

—————————

Fuente:

https://www.odoo.com/forum/help-1/question/how-to-add-wkhtmltopdf-as-a-sytem-parameter-in-openerp-56486#answer-56501

Odoo: Cambiar formato de Moneda de 000000.00 L a L 000,000,000.00

En la localización actual, viene por default que el formato de moneda sea 000000.00 L.

Entonces, para cambiarlo a L 000,000,000.00 se deben realizar los siguientes cambios en la configuración:

Con un usuario con características técnicas habilitadas:

  1. Contabilidad / varios / monedas: Para la moneda en cuestión, cambiar la posición del símbolo a: Antes de la cantidad.
  2. Configuración / Idiomas: Para el idioma requerido, cambiar el Formato separador a [3,3,3,3,-1] donde  3 indica en qué posición irá el separador de miles a partir de la última posición  en la que fue puesto; y -1 indica que se detenga ahí.

screenshot3

screenshot2

Configurar Odoo con nginx en Ubuntu

Install and Configure Nginx

$ sudo apt-get install nginx

Generate ssl certificate

$ sudo mkdir /etc/nginx/ssl
$ cd /etc/nginx/ssl
$ sudo openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
$ sudo openssl rsa -passin pass:x -in server.pass.key -out server.key
$ sudo rm server.pass.key
$ sudo openssl req -new -key server.key -out server.csr
$ sudo openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

Create Nginx server block

$ sudo vim /etc/nginx/sites-available/yourOdooSite.com
upstream oddo {
    server 127.0.0.1:8069;
}

server {
    listen      443 default;
    server_name yourOdooSite.com;

    access_log  /var/log/nginx/oddo.access.log;
    error_log   /var/log/nginx/oddo.error.log;

    ssl on;
    ssl_certificate     /etc/nginx/ssl/server.crt;
    ssl_certificate_key /etc/nginx/ssl/server.key;
    keepalive_timeout   60;

    ssl_ciphers             HIGH:!ADH:!MD5;
    ssl_protocols           SSLv3 TLSv1;
    ssl_prefer_server_ciphers on;

    proxy_buffers 16 64k;
    proxy_buffer_size 128k;

    location / {
        proxy_pass  http://oddo;
        proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
        proxy_redirect off;

        proxy_set_header    Host            $host;
        proxy_set_header    X-Real-IP       $remote_addr;
        proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header    X-Forwarded-Proto https;
    }

    location ~* /web/static/ {
        proxy_cache_valid 200 60m;
        proxy_buffering on;
        expires 864000;
        proxy_pass http://oddo;
    }
}

server {
    listen      80;
    server_name yourOdooSite.com;

    add_header Strict-Transport-Security max-age=2592000;
    rewrite ^/.*$ https://$host$request_uri? permanent;
}

Activate the server block by creating a symbolic link and restart nginx

$ sudo ln -s /etc/nginx/sites-available/yourOdooSite.com /etc/nginx/sites-enabled/yourOdooSite.com
$ sudo /etc/init.d/nginx restart

——————————–

Fuente: https://www.rosehosting.com/blog/install-odoo-formerly-openerp-with-nginx-on-an-ubuntu-vps/

———————————-

Subdominios:

Para que pueda redirigir de http a https cuando se usan subdominios; por ejemplo: subdomain.misite.com

En la sección de configuración de http, es necesario hacer el siguiente cambio:

server {
    listen      80;
    server_name yourOdooSite.com *.yourOdooSite.com;

    add_header Strict-Transport-Security max-age=2592000;
    rewrite ^/.*$ https://$host$request_uri? permanent;
}

Domain based db filter in odoo

You can use server startup with parameter –db-filter as: ‘%d’ or ‘^%d’ or even ‘^%d$’

The value is a regular expression, you may use %d for a hostname part of domain or %h to full domain name.

E.g.

domain = hostname.domainname.com

OpenERP replaces like:

%d = hostname

%h = hostname.domainname.com

The ‘^’ means start of line and the ‘$’ end of line, so ‘^%d$’ only filter db with name ‘hostname’ following the example.

Starting the server with:

openerp-server --db-filter='^%d'

Filter name of any db starting with «hostname» domain part.

 

From:

https://www.odoo.com/forum/help-1/question/domain-based-db-filter-6583

Instalar y configurar un servidor DNS con Ubuntu Server paso a paso

Slice of Linux

Un servidor DNS(Domain Name System) es un sistema que nos permite usar nombres de dominio en lugar de direcciones IP. Su principal ventaja es que para nosotros es mucho más fácil recordar un nombre que una dirección IP.

El servidor DNS más utilizado es Bind y, aunque teníamos un poco abandonado a Ubuntu Server en Slice of Linux, hoy vamos a ver cómo instalarlo y configurarlo sobre él paso a paso. El contenido de este tutorial es genérico pero las pruebas y capturas de pantalla se han hecho sobre Ubuntu 10.04 Server.

Los valores que debemos tener claros antes de comenzar son los siguientes:

  • Dirección IP del servidor: 192.168.2.1
  • Nombre del servidor: servidor
  • Dominio que vamos a crear: sliceoflinux.lan

Estos valores deberemos sustituirlos por los que necesitemos en cada caso.

Ver la entrada original 640 palabras más