Aaron Benton es un arquitecto experimentado especializado en soluciones creativas para desarrollar aplicaciones móviles innovadoras. Tiene más de 10 años de experiencia en desarrollo full stack, incluyendo ColdFusion, SQL, NoSQL, JavaScript, HTML y CSS. Aaron es actualmente Arquitecto de Aplicaciones para Shop.com en Greensboro, Carolina del Norte y es un Campeón de la comunidad Couchbase.

Para nuestro último post en el FakeIt serie vamos a explorar cómo podemos aprovechar FakeIt + Servidor Couchbase + Pasarela de sincronización para poner en marcha nuestro entorno local para el desarrollo móvil. Para ello utilizaremos Docker y docker-compose. Descargo de responsabilidad: No soy en absoluto un experto en Docker, esto es simplemente un ejemplo de lo que he hecho para configurar rápidamente un entorno de desarrollo y un conjunto de datos.
Docker
Utilizaremos dos contenedores Docker, uno para Servidor Couchbase y uno para Pasarela de sincronización. Podríamos definir un archivo docker-compose.yaml que simplemente tirara de couchbase:latest y couchbase/sync-gateway:latest pero aún habría necesidad de configuración manual y queremos ser capaces de automatizar tanto como sea posible para nuestra aplicación. Para ello tendremos que construir nuestros propios contenedores a partir de ambos añadiendo nuestros propios scripts y configuración.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
./docker-compose.yaml version: '2' services: fakeit-couchbase: build: context: ./.docker/couchbase/ container_name: fakeit-couchbase ports: - "8091-8094:8091-8094" - "11210:11210" volumes: - ./:/app fakeit-syncgatway: build: context: ./.docker/sync-gateway/ container_name: fakeit-syncgateway depends_on: - fakeit-couchbase ports: - "4984-4985:4984-4985" volumes: - ./:/app |
Nuestro archivo docker-compose.yaml primero construye un contenedor en el Dockerfile desde ./.docker/couchbase/Dockerfile que se ve así.
|
1 2 3 4 5 6 7 8 9 10 |
./.docker/couchbase/Dockerfile # start with couchbase FROM couchbase:latest # copy the configure script COPY scripts/configure-node.sh /opt/couchbase # execute the configure-node.sh script CMD ["/opt/couchbase/configure-node.sh"] |
Este Dockerfile en realidad sólo hace dos cosas, copiar un script de configuración y ejecutar ese script. El script configure-node.sh tiene este aspecto.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
./.docker/couchbase/scripts/configure-node.sh set -m /entrypoint.sh couchbase-server & echo 'Waiting 20 seconds for Couchbase service to start' sleep 20 # configure the cluster echo 'Configuring Cluster' /opt/couchbase/bin/couchbase-cli cluster-init -c localhost:8091 --cluster-username=Administrator --cluster-password=password --cluster-port=8091 --cluster-ramsize=500 --service=data # create the ecommerce bucket echo 'Creating ecommerce bucket' /opt/couchbase/bin/couchbase-cli bucket-create -c localhost:8091 -u Administrator -p password --bucket=ecommerce --bucket-eviction-policy=fullEviction --bucket-type=membase --bucket-priority=high --enable-index-replica=0 --bucket-port=11211 --enable-flush=1 --bucket-replica=1 --bucket-ramsize=200 echo 'Couchbase server is ready' fg 1 |
El script configure-node.sh está haciendo un par de cosas:
- Esperando a que se inicie el servicio Couchbase para poder configurarlo.
- Inicializar la configuración del Cluster
- Crear nuestro cubo de comercio electrónico
El contenedor Couchbase ya está construido, el siguiente contenedor que necesita ser construido es el contenedor Sync Gateway. Por defecto el contenedor Sync Gateway utiliza el bucket sync gateway de sólo memoria walrus. Tendremos que actualizar esta configuración proporcionando nuestro propio archivo sync-gateway.json para que podamos actualizar la configuración de almacenamiento y acceso. Por último, el contenedor Sync Gateway sólo expone el puerto 4984, que es el puerto público, como esto es para fines de desarrollo vamos a seguir adelante y exponer el puerto 4985 que es el puerto de administrador.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
./.docker/sync-gateway/Dockerfile # Start with the base sync gateway FROM couchbase/sync-gateway:latest # Copy the sync-gateway.json into the container COPY sync-gateway.json /opt/sync_gateway/sync-gateway.json # Create Volume for data to persist RUN mkdir -p /opt/sync_gateway/data # Copy Entry Point COPY scripts/entrypoint.sh / ENTRYPOINT ["/entrypoint.sh"] # Copy the configure script COPY scripts/configure-node.sh /opt/sync_gateway # Configure the Sync Gateway and Start it CMD ["/opt/sync_gateway/configure-node.sh"] # port 4984: public port # port 4985: admin port EXPOSE 4984 4985 ./.docker/sync-gateway/sync-gateway.json { "interface": "0.0.0.0:4984", "adminInterface": "0.0.0.0:4985", "log": ["CRUD+", "REST+", "Changes+", "Attach+"], "CORS": { "Origin":[ "https://localhost:8000", "*" ], "LoginOrigin":[ "https://localhost:8000", "*" ], "Headers":["Content-Type"], "MaxAge": 1728000 }, "databases": { "ecommerce": { "server": "https://fakeit-couchbase:8091", "bucket": "ecommerce", "users": { "GUEST": { "disabled": false } }, "sync": "function(doc, oldDoc) { channel(doc.channels); }" } } } ./.docker/sync-gateway/scripts/configure-node.sh #!/bin/bash set -m echo 'Waiting 20 seconds for Couchbase service to start and warm up' sleep 20 echo 'Starting the Sync Gateway Service' /entrypoint.sh sync_gateway /opt/sync_gateway/sync-gateway.json echo 'Couchbase Sync Gateway is ready' |
Una vez más, esto es sólo para fines de desarrollo. Usted nunca debe permitir el acceso desde cualquier lugar a su adminInterface o habilitar INVITADO acceso a tu sync-gateway a menos que haya una muy buena razón para hacerlo.
La estructura de nuestra aplicación tiene ahora este aspecto:

Ahora que hemos configurado nuestros contenedores Docker y nuestro archivo docker-compose.yaml necesitamos construir e iniciar los contenedores. Para ello, ejecutamos el siguiente comando desde nuestro directorio de aplicaciones:
|
1 |
docker-compose up -d |
Nota: para los propósitos del screencast, se omite el parámetro -d para ejecutar los contenedores en modo separado.

Modelos
Nuestros contenedores ya están iniciados, lo siguiente que tenemos que hacer antes de generar nuestro conjunto de datos es actualizar los modelos para que admitan el atributo channels.
|
1 |
users.yaml |
Este modelo sólo se sincronizará con canales específicos del usuario.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
name: Users type: object key: _id data: min: 1000 max: 2000 inputs: ./countries.json properties: _id: type: string description: The document id built by the prefix "user_" and the users id data: post_build: `user_${this.user_id}` channels: type: array data: post_build: | return [ `channel-user-${this.user_id}` ]; doc_type: type: string description: The document type data: value: "user" ... products.yaml |
Sólo por diversión publicaremos este modelo en un canal global al que estarán suscritos todos nuestros usuarios.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
name: Products type: object key: _id data: min: 500 max: 1000 inputs: - ./categories.csv pre_build: globals.current_category = faker.random.arrayElement(inputs.categories); properties: _id: type: string description: The document id data: post_build: `product_${this.product_id}` channels: type: array data: build: | return [ `channel-products` ]; doc_type: type: string description: The document type data: value: product ... orders.yaml |
Este modelo sólo se sincronizará con canales específicos del usuario.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
name: Orders type: object key: _id data: dependencies: - products.yaml - users.yaml min: 5000 max: 6000 properties: _id: type: string description: The document id data: post_build: `order_${this.order_id}` channels: type: array data: build: | return [ `channel-user-${this.user_id}` ]; doc_type: type: string description: The document type data: value: "order" ... |
Ahora que nuestros modelos han sido actualizados para soportar canales, podemos generar nuestro conjunto de datos aleatorios e introducirlo en Servidor Couchbase a través de la Pasarela de sincronización API REST. Le decimos FakeIt para ello, utilice el siguiente comando:
|
1 |
fakeit sync-gateway --server https://localhost:4984 --bucket ecommerce --verbose models/* |
Para fines de desarrollo hemos permitido el acceso de invitados a nuestro Pasarela de sincronización. Sin embargo, si tiene desactivado el acceso de invitados, puede seguir utilizando FakeIt especificando un nombre de usuario y una contraseña a un usuario existente mediante el siguiente comando:
|
1 |
fakeit sync-gateway --server https://localhost:4984 --bucket ecommerce --username YOURUSERNAME --password YOURPASSWORD --verbose models/* |
Antes de dar salida al conjunto de datos generado, FakeIt se autenticará contra el Pasarela de sincronización para recuperar la información de sesión necesaria.

Pruebas
Lo siguiente que tenemos que hacer es crear un usuario de pasarela de sincronización para que podamos sincronizar sus documentos localmente. Para esta prueba, vamos a coger un documento de usuario aleatorio de nuestro modelo Users y crearemos un usuario a partir de ese documento. En este ejemplo será user_1001, y crearemos el usuario usando el comando curl:
|
1 2 3 4 5 6 |
curl --silent --show-error \ -H "Content-Type: application/json; charset=UTF-8" \ -H "Content-type: application/json" \ -X PUT \ -d '{"name":"Domenic81","password":"pgid_Tubn0qoEtZ","admin_channels":["channel-user-1001", "channel-products"]}' \ 'https://localhost:4985/ecommerce/_user/Domenic81' |

Aplicación
Hemos creado un pequeño VueJS proyecto que utiliza PouchDB para conectarse al Sync Gateway y extraer los documentos de un usuario autenticado. Nuestra aplicación sólo va a mostrar los diferentes tipos de documentos disponibles, sus IDs y contenido.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
// register highlight.js vue component Vue.use(VueHighlightJS) // create local ecommerce database var db = new PouchDB('local_ecommerce', { auto_compaction: true, revs_limit: 3 }); // create remote ecommerce database var remote_db = new PouchDB('https://localhost:4984/ecommerce/', { auth: { username: 'Domenic81', password: 'pgid_Tubn0qoEtZ' }, skip_setup: true }); // merge remote database with local database PouchDB.sync(db, remote_db, { live: true, retry: true }) var app = new Vue({ el: '#app', data: { current_type: '', items: [], id: '', current: JSON.stringify({}), types: [], }, watch: { id(id) { if (id) { db.get(id).then((current) => this.current = JSON.stringify(current, null, 2)) } }, current_type(type) { this.id = '' // update the current item to be blank this.current = JSON.stringify({}) // when `current_type` changes update the items list with the current types list this.updateItems(type) .then(() => { if (!this.id) { this.id = this.items[0] } }) } }, methods: { updateItems(type) { return db.query('ecommerce/by_doc_type', { key: type, reduce: false }) .then((result) => { this.items = result.rows.map((item) => item.id) }) }, removeStyle(el) { setTimeout(() => { el.style = '' }, 1000) } }, created() { // create the view to be queried var ddoc = { _id: '_design/ecommerce', views: { by_doc_type: { map: function(doc) { if (doc.doc_type) { emit(doc.doc_type); } }.toString(), reduce: '_count' } } } db.put(ddoc) // design document created // call the design document view index immediately to trigger a build .then(() => db.query('ecommerce/by_doc_type', { limit: 0 })) .catch((err) => { // it is fine if this fails and returns a document conflict that just means it doesnt need to be created if (err.status !== 409) { throw err; } }) // get all of the doc types available .then(() => db.query('ecommerce/by_doc_type', { reduce: true, group: true })) .then((types) => { this.types = types.rows.map((type) => type.key) this.current_type = this.types[0] }) } }) |

La aplicación de muestra completa puede consultarse en https://github.com/bentonam/fakeit-couchbase-mobile-example
Conclusión
A lo largo de esta serie han visto cómo FakeIt puede tomar simples modelos YAML y generar grandes cantidades de datos falsos y enviar esos datos a múltiples destinos diferentes. Por favor, consulte el repositorio, pull requests son bienvenidos, siempre estamos buscando mejoras y mejoras para hacer la herramienta más útil para la comunidad. También me gustaría aprovechar este momento para agradecer a aquellos que han hecho contribuciones al proyecto. La versión 1.0 no se habría publicado sin la ayuda de Tyler Benton (@tjbenton21), también Trevor Brindle (@VinceKerrazzi), Jessica Kennedy (@mistersender), Adam Burdette (@RavenStorms619) y Brant Burnett (@btburnett3)
Anterior
- FakeIt Serie 1 de 5: Generación de datos falsos
- FakeIt Serie 2 de 5: Datos compartidos y dependencias
- FakeIt Series 3 de 5: Modelos Lean a través de definiciones
- FakeIt Series 4 de 5: Trabajar con datos existentes
