Incorporación de desencadenadores a una operación

Completado

Los desencadenadores son la forma principal en la que Azure Cosmos DB for NoSQL puede insertar lógica de negocios antes y después de las operaciones. Los desencadenadores son recursos almacenados en un contenedor, y su código se escribe en JavaScript, de forma muy similar a los procedimientos almacenados y las funciones definidas por el usuario.

Los desencadenadores se definen como funciones de JavaScript. Luego, la función se ejecuta cuando se invoca al desencadenador.

function name() {
}

Dentro de la función, el método getContext() recupera un objeto de contexto que se puede usar para realizar varias acciones, que incluyen:

  • Acceder al objeto de solicitud HTTP (el origen de un desencadenador previo)

  • Acceder al objeto de respuesta HTTP (el origen de un desencadenador posterior)

  • Acceder al contenedor correspondiente de Azure Cosmos DB for NoSQL

Con el objeto de contexto puede invocar a los métodos getRequest() o getResponse() para acceder a los objetos de solicitud y respuesta HTTP. También puede invocar al método getCollection() para acceder al contenedor mediante la API de consulta de JavaScript.

Desencadenador previo

Los desencadenadores previos se ejecutan antes de una operación y no pueden tener ningún parámetro de entrada. Pueden realizar acciones como validar las propiedades de un elemento o insertar propiedades que faltan.

Vamos a realizar un ejemplo sencillo en el que hay un elemento JSON listo para crearse en un contenedor.

{
  "id": "caab0e5e-c037-48a4-a760-140497d19452",
  "name": "Handlebar",
  "categoryId": "e89a34d2-47ee-4da8-bcf6-10f552604b79",
  "categoryName": "Accessories",
  "price": 50
}

En este ejemplo se crea un desencadenador previo que se ejecuta antes de una operación HTTP POST. Este desencadenador comprueba la existencia de una propiedad label. Si no existe, agrega la propiedad label con un valor de new. El código de JavaScript de esta función usa los métodos getContext() y getRequest() para obtener la solicitud HTTP actual y luego el cuerpo de la solicitud.

function addLabel() {
    var context = getContext();
    var request = context.getRequest();
    
    var pendingItem = request.getBody();
}

Por último, la función comprueba la existencia de la propiedad label, la agrega si no existe y devuelve el elemento modificado como cuerpo de la solicitud actualizado.

if (!('label' in pendingItem))
    pendingItem['label'] = 'new';

request.setBody(pendingItem);

La función de desencadenador previo final contiene el código siguiente:

function addLabel() {
    var context = getContext();
    var request = context.getRequest();
    
    var pendingItem = request.getBody();

    if (!('label' in pendingItem))
        pendingItem['label'] = 'new';

    request.setBody(pendingItem);
}

Si invoca a la operación create mediante este desencadenador previo, debe esperar que el JSON resultante incluya la propiedad label gracias a la lógica del desencadenador.

{
  "id": "caab0e5e-c037-48a4-a760-140497d19452",
  "name": "Handlebar",
  "categoryId": "e89a34d2-47ee-4da8-bcf6-10f552604b79",
  "categoryName": "Accessories",
  "price": 50,
  "label": "new"
}

Desencadenador posterior

Los desencadenadores posteriores se ejecutan después de que se haya completado una operación y pueden tener parámetros de entrada, aunque no son necesarios. Realizan una acción en el mensaje de respuesta HTTP justo antes de que se envíe al cliente. Pueden realizar acciones como la actualización o la creación de elementos secundarios en función de los cambios efectuados en el elemento original.

Vamos a realizar un ejemplo ligeramente diferente con el mismo archivo JSON. Ahora se va a usar un desencadenador posterior para crear un segundo elemento con una vista materializada diferente de los datos. El objetivo es crear un segundo elemento con tres propiedades JSON: sourceId, categoryId y displayName.

{
  "sourceId": "caab0e5e-c037-48a4-a760-140497d19452",
  "categoryId": "e89a34d2-47ee-4da8-bcf6-10f552604b79",
  "displayName": "Handlebar [Accessories]",
}

Nota:

Se incluye la propiedad categoryId porque todos los elementos creados en un desencadenador posterior deben tener la misma clave de partición lógica que el elemento original que ha sido el origen del desencadenador.

Se puede iniciar la función con la obtención del contenedor y la respuesta HTTP mediante los métodos getCollection() y getResponse(). También se obtiene el elemento recién creado mediante el método getBody() del objeto de respuesta HTTP.

function createView() {
    var context = getContext();
    var container = context.getCollection();
    var response = context.getResponse();
    
    var createdItem = response.getBody();
}

Si se usan las distintas propiedades del elemento recién creado se puede construir un nuevo objeto de JavaScript.

var viewItem = {
    sourceId: createdItem.id,
    categoryId: createdItem.categoryId,
    displayName: `${createdItem.name} [${createdItem.categoryName}]`
};

Luego se puede usar el método createDocument para crear un nuevo elemento a partir de la vista y producir o devolver si hay errores o tiempos de espera.

var accepted = container.createDocument(
    container.getSelfLink(),
    viewItem,
    (error, newItem) => {
        if (error) throw error;
    }
);
if (!accepted) return;

La función de desencadenador posterior final contiene el código siguiente:

function createView() {
    var context = getContext();
    var container = context.getCollection();
    var response = context.getResponse();
    
    var createdItem = response.getBody();
    
    var viewItem = {
        sourceId: createdItem.id,
        categoryId: createdItem.categoryId,
        displayName: `${createdItem.name} [${createdItem.categoryName}]`
    };
 
    var accepted = container.createDocument(
        container.getSelfLink(),
        viewItem,
        (error, newItem) => {
            if (error) throw error;
        }
    );
    if (!accepted) return;
}