No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Consideraciones al hacer migraciones

26/27
Recursos

Aportes 25

Preguntas 14

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Me carga el payaso… tengo más de 20 migraciones porque me puse a jugar con las tablas y las columnas en clases anteriores. Me tocó hacer una faena épica.

Mi archivo único de migraciones es algo como esto:

'use strict';
const { DataTypes, Sequelize } = require('sequelize');
const { USER_TABLE, UserSchema } = require('../models/userModel')
const { CUSTOMER_TABLE, CustomerSchema } = require('../models/customerModel')
const { CATEGORY_TABLE, CategorySchema } = require('../models/categoryModel')
const { PRODUCT_TABLE, ProductSchema } = require('../models/productModel')
const { ORDER_TABLE } = require('../models/orderModel')
const { OrderProductSchema, ORDER_PRODUCT_TABLE } = require('../models/order-productModel')


module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, UserSchema);
    await queryInterface.createTable(CUSTOMER_TABLE, CustomerSchema);
    await queryInterface.createTable(CATEGORY_TABLE, CategorySchema);
    await queryInterface.createTable(PRODUCT_TABLE, ProductSchema);
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id'
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
          },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
        },
    });
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, OrderProductSchema);

  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE)
    await queryInterface.dropTable(CUSTOMER_TABLE)
    await queryInterface.dropTable(CATEGORY_TABLE)
    await queryInterface.dropTable(PRODUCT_TABLE)
    await queryInterface.dropTable(ORDER_TABLE)
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE)
  }
};

Grande Nicolás! Te la comiste con este curso. Mil gracias, aprendí un montón!

este curso está 10/10 ☄

En mi opinión (para este caso) es mucho más sencillo borrar todas las migraciones y crear una sola en limpio con su respectivo código.

Con esto no habría ningún inconveniente al subirlo a producción. Claro, habría otras complicaciones más adelante en Development, pero si no tienen problema, pueden borrar y crear nuevamente las tablas de la base de datos.

Las migraciones son muy delicadas, es por ello que se deben tener en cuenta los esquemas. A nivel producción no se recomienda crear migraciones paso a paso, más bien, crear una sola migración que ya tenga todas las relaciones y configuración por defecto.

Una buena practica es no repetir el código, pero en este caso se va necesitar repetir el esquema cuando se creen las tablas.

Cada vez que agregamos un atributo o algo parecido, lo mejor es no basarse en el esquema porque es algo genérico y no está versionado, sirve para el modelo, pero no es buena practica que sea la base de las migraciones porque lo configuramos en cualquier momento y puede dañar la configuración.

Los archivos que se cambiaron fueron los siguientes:

Migración create-user.js:

'use strict';

const { USER_TABLE } = require('../models/user.model');
const { DataTypes, Sequelize } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      email: {
        allowNull: false,
        type: DataTypes.STRING,
        unique: true,
      },
      password: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE);
  },
};

Migración add-role.js:

'use strict';

const { USER_TABLE } = require('../models/user.model');
const { DataTypes } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.addColumn(USER_TABLE, 'role', {
      allowNull: false,
      type: DataTypes.STRING,
      defaultValue: 'customer',
    });
  },

  down: async (queryInterface) => {
    await queryInterface.removeColumn(USER_TABLE, 'role');
  },
};

Migración create-customers.js:

'use strict';

const { CUSTOMER_TABLE } = require('../models/customer.model');
const { USER_TABLE } = require('../models/user.model');
const { DataTypes, Sequelize } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(CUSTOMER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      lastName: {
        allowNull: false,
        type: DataTypes.STRING,
        field: 'last_name',
      },
      phone: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
      },
      userId: {
        field: 'user_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: USER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(CUSTOMER_TABLE);
  },
};

Migración change-user-id.js:

'use strict';

const { CUSTOMER_TABLE } = require('../models/customer.model');
const { DataTypes } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.changeColumn(CUSTOMER_TABLE, 'user_id', {
      field: 'user_id',
      allowNull: false,
      type: DataTypes.INTEGER,
      unique: true,
    });
  },

  down: async (queryInterface) => {
    // await queryInterface.dropTable(CUSTOMER_TABLE);
  },
};

Migración order.js:

'use strict';

const { ORDER_TABLE } = require('../models/order.model');
const { CUSTOMER_TABLE } = require('../models/customer.model');
const { DataTypes, Sequelize } = require('sequelize');

module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(ORDER_TABLE);
  },
};

Con este curso me he dado cuenta de que el Frontend es definitivamente más fácil que el Backend 😅

Error ''VIRTUAL'

Con la configuración de ssl del video no me ha funcionado. Parece que para un pool si sirve poner:

dialectOptions: {
            ssl: {
                rejectUnauthorized: false
            }
        }

Pero me seguía dando un error de pg_hba.conf y ssl en off. He tenido que añadir:

dialectOptions: {
            ssl: {
                require: true,
                rejectUnauthorized: false
            }
        }

Me seguía falando y encontré un aporte que indicaba que había que definir el modo:

heroku config:set PGSSLMODE=no-verify

Porfin de verdad con este curso sufri mucho pero creo que valio la pena se siente tambien poder decir lo logre, gracias platzi , gracias al profesor fue muy claro ayudo un monton y austedes la comunidad cada vez que me encontraba un problea casi siempre habia alguien comentando al respecto y ayudo mucho la verdad… a por mas si llegaste a leer esto es porque tu tambien lo lograste muchas felicidades para ti tambien.

Heroku no tienes planes gratuito a la fecha! hice mi deploy en render el repositorio de github

No me quedaré con la duda. Lo que entiendo es que las migraciones en la pratica nos servirán para que se creen todas las tablas en nuestra base de datos nueva. ¿Estoy bien en eso?

Si desde el inicio ya tengo claro como quiero que sea mi base de datos y no tengo que hacer fixes porque desde el inicio se lo que se iba a presentar. ¿Podría simplemente eliminar la creación de crear el rol puesto que si ya estaba en el esquema en la migración anterior se crearía?

No se si me hago entender, me gustaría salir de esa duda.

La funcionalidad del rango de precios de productos quedó mal 😦

Uff ya quiero trabajar en otro proyecto igual.

Vaya a mi me paso eso, pero hice el consejo que dio el profesor en la anterior clase, entre a la base de datos y borre la columna role, y volvi a lanzar la migracion para que cree la columna rol y solucionado

Mi solución al reto en clase 😄

"use strict";

const { DataTypes } = require("sequelize");
const {
  CATEGORY_TABLE,
  CategorySchema,
} = require("../models/categories.model");
const { CustomerSchema, CUSTOMER_TABLE } = require("../models/customers.model");
const {
  ORDER_PRODUCT_TABLE,
  OrderProductSchema,
} = require("../models/orders-products.model");
const { ORDER_TABLE } = require("../models/orders.model");
const { PRODUCT_TABLE, ProductSchema } = require("../models/products.model");
const { USER_TABLE, UserSchema } = require("../models/users.model");

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  async up(queryInterface) {
    await queryInterface.createTable(CATEGORY_TABLE, CategorySchema);
    await queryInterface.createTable(PRODUCT_TABLE, ProductSchema);
    await queryInterface.createTable(USER_TABLE, UserSchema);
    await queryInterface.createTable(CUSTOMER_TABLE, CustomerSchema);
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        primaryKey: true,
        autoIncrement: true,
        type: DataTypes.INTEGER,
      },
      customerId: {
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: "id",
        },
        onUpdate: "CASCADE",
        onDelete: "CASCADE",
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
      },
      updatedAt: {
        allowNull: false,
        type: DataTypes.DATE,
      },
    });
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, OrderProductSchema);
  },

  async down(queryInterface) {
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE);
    await queryInterface.dropTable(ORDER_TABLE);
    await queryInterface.dropTable(CUSTOMER_TABLE);
    await queryInterface.dropTable(USER_TABLE);
    await queryInterface.dropTable(PRODUCT_TABLE);
    await queryInterface.dropTable(CATEGORY_TABLE);
  },
};

Gracias Nico por el curso, estuvo retador y aprendí mucho!!. Sin embargo decidí usar Prisma como ORM por diferentes razones, una de ellas fue que cierta syntax que usamos en el curso cambio un poco y me causo varios problemas. También el manejo de migraciones y conexiones es más sencilla.

Para quienes tengan algo de conocimiento en base de datos prácticamente las migraciones es lo mismo que ejecutar DDL para modificar una base de datos relacional, por lo que igualmente a veces depende de las actualizaciones a aplicar se ejecuta un solo script con todos los comandos para las mismas… Y dichos comandos igualmente se debe ser bastante cuidadosos para primero analizar las tablas a modificar y en base a ello realizar los ajustes, por lo que haciendo un paralelismo las migraciones son exactamente lo mismo solo que sin aplicar ningún comando SQL/DDL

Les comparto como pude desplegar mi API de forma gratuita:

Aloje mi PostgresDB en Vercel que da una opción gratuita hasta 256mb.

En cuanto al Node.js lo puede subir a Cyclic.

Recuerden configurar el Enviroment que provee Vercel para que se pueda conectar correctamente,

Por último hacen la migración completa de la arquitectura de la tabla a VercelDB.

Después de esto ya pueden empezar a usar Insómania para manipular la API.

Saludos!!

DE ESTA MANERA SUBIMOS A Railway:
De esta manera pude yo subirlo a Railway sin borrar el .env del .gitignore Configure las migraciones a una sola:

'use strict';

const { DataTypes, Sequelize } = require('sequelize');
const { USER_TABLE } = require('./../models/user.model');
const { CUSTOMER_TABLE } = require('./../models/customer.model');
const { CATEGORY_TABLE } = require('./../models/category.model');
const { PRODUCT_TABLE } = require('./../models/product.model');
const { ORDER_TABLE } = require('./../models/order.model');
const { ORDER_PRODUCT_TABLE } = require('./../models/order-product.model');

/** @type {import('sequelize-cli').Migration} */
module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      email: {
        allowNull: false,
        type: DataTypes.STRING,
        unique: true,
      },
      password: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
    });
    await queryInterface.createTable(CUSTOMER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      firstName: {
        allowNull: false,
        type: DataTypes.STRING,
        field: 'first_name',
      },
      lastName: {
        allowNull: false,
        type: DataTypes.STRING,
        field: 'last_name',
      },
      phone: {
        allowNull: true,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
      userId: {
        field: 'user_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: USER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
    await queryInterface.createTable(CATEGORY_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        allowNull: false,
        unique: true,
        type: DataTypes.STRING,
      },
      image: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      createdAt: {
        allowNull: false,
        defaultValue: Sequelize.NOW,
        field: 'create_at',
        type: DataTypes.DATE,
      },
    });
    await queryInterface.createTable(PRODUCT_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      name: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      image: {
        allowNull: false,
        type: DataTypes.STRING,
      },
      description: {
        allowNull: false,
        type: DataTypes.TEXT,
      },
      price: {
        allowNull: false,
        type: DataTypes.INTEGER,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
      categoryId: {
        field: 'category_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CATEGORY_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      createdAt: {
        allowNull: false,
        defaultValue: Sequelize.NOW,
        field: 'create_at',
        type: DataTypes.DATE,
      },
    });
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER,
      },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'create_at',
        defaultValue: Sequelize.NOW,
      },
      amount: {
        allowNull: false,
        type: DataTypes.INTEGER,
      },
      orderId: {
        field: 'order_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: ORDER_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
      productId: {
        field: 'product_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: PRODUCT_TABLE,
          key: 'id',
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL',
      },
    });
  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE);
    await queryInterface.dropTable(CUSTOMER_TABLE);
    await queryInterface.dropTable(CATEGORY_TABLE);
    await queryInterface.dropTable(PRODUCT_TABLE);
    await queryInterface.dropTable(ORDER_TABLE);
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE);
  },
};

Se crean un archivo **Dockerfile ** en la raíz del proyecto:

FROM node:16 AS build

RUN mkdir -p /usr/src/app

WORKDIR /usr/src/app

ARG NODE_ENV
ARG DATABASE_URL

ENV NODE_ENV=$NODE_ENV
ENV DATABASE_URL=$DATABASE_URL

COPY package*.json ./
RUN npm ci

COPY . .

RUN npm run migrations:run

EXPOSE 8080

CMD ["npm", "start"]

Primero creamos una DB en Railway y copiamos la URL de conexión en su apartado de conexión , le dan a New , luego Database - Postgres y le tiene que quedar como la imagen, Las variables de entorno se configuran automaticamente:

Una opción de deploy es Railway yo lo use así:

  • Primero deben de configuarar las migraciones en mi caso borre todas las migraciones y solo cree una:
'use strict';
const { DataTypes, Sequelize } = require('sequelize');
const { USER_TABLE, UserSchema } = require('./../models/user.model')
const { CUSTOMER_TABLE, CustomerSchema } = require('./../models/customer.model')
const { CATEGORY_TABLE, CategorySchema } = require('./../models/category.model')
const { PRODUCT_TABLE, ProductSchema } = require('./../models/product.model')
const { ORDER_TABLE } = require('./../models/order.model')
const { OrderProductSchema, ORDER_PRODUCT_TABLE } = require('./../models/order-product')



module.exports = {
  up: async (queryInterface) => {
    await queryInterface.createTable(USER_TABLE, UserSchema);
    await queryInterface.createTable(CUSTOMER_TABLE, CustomerSchema);
    await queryInterface.createTable(CATEGORY_TABLE, CategorySchema);
    await queryInterface.createTable(PRODUCT_TABLE, ProductSchema);
    await queryInterface.createTable(ORDER_TABLE, {
      id: {
        allowNull: false,
        autoIncrement: true,
        primaryKey: true,
        type: DataTypes.INTEGER
      },
      customerId: {
        field: 'customer_id',
        allowNull: false,
        type: DataTypes.INTEGER,
        references: {
          model: CUSTOMER_TABLE,
          key: 'id'
        },
        onUpdate: 'CASCADE',
        onDelete: 'SET NULL'
          },
      createdAt: {
        allowNull: false,
        type: DataTypes.DATE,
        field: 'created_at',
        defaultValue: Sequelize.NOW,
        },

    });
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, OrderProductSchema);

  },

  down: async (queryInterface) => {
    await queryInterface.dropTable(USER_TABLE)
    await queryInterface.dropTable(CUSTOMER_TABLE)
    await queryInterface.dropTable(PRODUCT_TABLE)
    await queryInterface.dropTable(CATEGORY_TABLE)
    await queryInterface.dropTable(ORDER_TABLE)
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE)
  }
};
  • Se crean un archivo Dockerfile en la raíz del proyecto, ya que Railway solo puede ejecutar este archivo de docker:
    Dockerfile
FROM node:16 AS build

RUN mkdir -p /usr/src/app

WORKDIR /usr/src/app

COPY package*.json ./
RUN npm ci

COPY . .

RUN npm run migrations:run

EXPOSE 8080

CMD ["npm", "start"]
  • Primero creamos una DB en Railway y copiamos la URL de conexión en su apartado de conexión
  • En esta parte debemos ir al archivo .env y pegar la url de conexión:
    .env
DATABASE_URL='urlderailway'
  • En nuestro .gitignore eliminamos el archivo .env y lo subimos a un repo de github. Y creamos el proyecto para nuestra aplicaión en Railway

  • Creamos el servicio y debería correr la migración sin problemas.

  • NOTA: esta opción nos dejará hacer el deploy pero es una mala practica ya que estaremos dejando expuesta nuestra varible de entorno al subir el archivo .env . Por lo que es solo una buena opción para aprender pero para producción no.
    No se si se podrá ya que al momento de crear el proyecto del servicio railway nos permite pasarle variables de entorno pero por alguna razón (en mi caso) no detectaba o no le llegaba la variable de entorno, es por eso, por el momento que lo solucione así, si alguien pudo subirlo en Railway espero su feedback. Gracias.

Qué pasaria si, borro la migración de add-role y dejo que el create-user cree el modelo como esta definido ya con el role?

Tuve un error al crear la migración ya que me aparecia un error con el autoIncrement en la primera tabla.
El error era el siguente:

ERROR: syntax error at or near "SERIAL"

Se pudo corregir dandole el valor de autoIncrement: false, y se agrega la propiedad de defaultValue: UUIDV4: Quedando de la siguente forma

id: {
        allowNull: false,
        autoIncrement: false,
        primaryKey: true,
        type: DataTypes.STRING,
        defaultValue: UUIDV4,
      } 

Tomen en cuenta cambiar todas los id de este tipo, también con la llaves foráneas.

Esta clase es como un backlog y documento de lecciones aprendidas pero en formato mp4

El contenido del archivo de migración única:

'use strict';


const { UserSchema, USER_TABLE } = require('./../models/userModel');
const { CustomerSchema, CUSTOMER_TABLE } = require('./../models/customerModel');
const { ProductSchema, PRODUCT_TABLE } = require('./../models/productModel');
const { CategorySchema, CATEGORY_TABLE } = require('./../models/categoryModel');
const { OrderSchema, ORDER_TABLE } = require('./../models/orderModel');
const { OrderProductSchema, ORDER_PRODUCT_TABLE } = require('./../models/order-productModel');

module.exports = {
  async up (queryInterface) {
    await queryInterface.createTable(USER_TABLE, UserSchema);
    await queryInterface.createTable(CUSTOMER_TABLE, CustomerSchema);
    await queryInterface.createTable(PRODUCT_TABLE, ProductSchema);
    await queryInterface.createTable(CATEGORY_TABLE, CategorySchema);
    await queryInterface.createTable(ORDER_TABLE, OrderSchema);
    await queryInterface.createTable(ORDER_PRODUCT_TABLE, OrderProductSchema);
  },

  async down (queryInterface) {
    await queryInterface.dropTable(USER_TABLE);
    await queryInterface.dropTable(CUSTOMER_TABLE);
    await queryInterface.dropTable(PRODUCT_TABLE);
    await queryInterface.dropTable(CATEGORY_TABLE);
    await queryInterface.dropTable(ORDER_TABLE);
    await queryInterface.dropTable(ORDER_PRODUCT_TABLE);
  }
};