Dominios de Diseño para desarrollo (Changelog, estándares y FAQ) === # Versión 0.1.20 [Cómo revisar las versiones del documento](https://hackmd.io/c/tutorials/%2Fs%2Fhow-to-save) # Estándares generales - [x] Los tests deben ejecutarse exitosamente tanto en api como en app para poder entregar - [x] No modificar la versión de la aplicación ni en el changelog ni en el package.json. - [x] Agregar el pedido desde el cual se originó el PR. - [x] La documentación y los comentarios deben estar en ingles, el changelog en español. - [x] Ejecutar en todos los archivos modificados eslint con las reglas definidas en configuración - [x] Usar correctamente [jsdoc](https://jsdoc.app/tags-param.html) al documentar objetos - Ejemplo ```javascript= // NO /** * Description * @param {Object} item */ const fun = (item) => { console.log(item.id) } // SÍ /** * Description * @param {Object} item * @param {string} item.id */ const fun = (item) => { console.log(item.id) } ``` - [x] Nombre de las variables, intuitivas, claras, cortas, correctamente escritas - [x] Funciones: Las variables opcionales van todas dentro de un objeto (ese objeto es opcional) y, si es que existe, siempre es el último argumento de la función. - [x] Las propiedades opcionales deben estar definidas en la firma de la función - Ejemplo ```javascript const fun = (oblig1, oblig2, opc1 = null) // NO const fun = (oblig1, oblig2, options = null) // NO const fun = (oblig1, oblig2, { opc1 } = {}) // NO const fun = (oblig1, oblig2, { opc1 = null } = {}) // SÍ ``` - [x] Las entregas de desarrollo deben incluir un link donde se pueda testear la nueva funcionalidad, utilizando la BD que definé el nacho en <https://demo.equipco.app/k43jn543jk5nINI63vBHJYhvjv67253hvjV62TVV27263Ljl723/rethinkdb_dump.tar.gz> sin ninguna alteración más que el updateDB que se ejecuta automáticamente al ejecutarse. - [X] Los archivos de audio, imágenes y adjuntos de Demo se pueden encontrar en el link <https://demo.equipco.app/k43jn543jk5nINI63vBHJYhvjv67253hvjV62TVV27263Ljl723/uploadedFilesBackup.tar.gz> - [x] No utilizar fechas dinámicas (new date) en los tests, ya que el comportamiento puede variar durante el fin de semana - [x] Al comparar 2 valores con `>`, `<`, `>=` o `<=`, si alguno de los valores puede ser `null` o `undefined`, entonces siempre tiene que haber una condición que se asegure de que ese no sea el caso antes de hacer la comparación. - Ejemplo: Si `deletedAt` puede ser un número o `null`, pero `createdAt` siempre es un número: - Incorrecto: `if(deletedAt > createdAt) ...` - Correcto: `if(deletedAt !== null && deletedAt > createdAt) ...` - [x] No reutilizar cases de distintas acciones en los reducers (getdocs, getActionError etc...) - [x] DRY (Don't Repeat Yourself): No repetir varias líneas de código - [x] Extrae código repetido a funciones helper - [x] No es problema repetir props o argumentos (?) - [ ] TODO: Ejemplos - [x] Antes de guardar números con decimales en la DB, redondear a 3 decimales usando `threeDecimals` - Excepto los valores de "cost" de las prioridades que se guardan en costAndTime (y constAndTimeByUser), que son string y se operan con bigNumber3S - [x] Al mostrar un número al usuario, redondear a 3 decimales y truncar a 2 decimales. No mostrar los ceros "trailing" - Usar función `round3ThenTrunc` con 2 decimales - Ejemplo: 1.1111 -> 1.11, 1.1011 -> 1.1, 1.0011 -> 1 - Excepciones de valores sin decimales (round3ThenTrunc con 0 decimales) - Los valores de "Actual" y "Proyectado" en el componente MonthlyCost (en la barra superior de admin) - En la tabla de videos vistos y acciones realizadas. - En el AppBar (HAPs, bono e indicadores). - En la tabla de áreas y usuarios de las pestañas de Equipo, Clientes y Admin. - En el gráfico de TDIs, la primera fila de los detalles el porcentaje y las horas - [x] Las acciones que tienen prefijo "GET_" (e.g. "GET_UPDATES") no pueden hacer cambio a la DB (dbChanges vacío) - Esto porque no se toman en cuenta en la revisión de acciones repetidas (pipeline) # Estándares de Eficiencia - [ ] No usar loops dentro de loops innecesariamente. - A cambio, usar estructuras que pueden hacer consultas sobre elementos identificados por una llave de manera eficiente, como `Object`, `Map` o `Set` (en caso de que la consulta sea la existencia de la llave). - No es necesario usar un loop interior si los elementos que se están revisando no varían en las distintas iteraciones del loop exterior - E.g. Agregar el atributo `isGuest` a usuarios en el arreglo `users`. El valor de `isGuest` depende de si el id del usuario se encuentra en el arreglo `guests` ```javascript // users: object[]. Arreglo de usuarios con id:string // guests: string[]. Arreglo de ids de usuarios // Ineficiente users.forEach(user => { // guests.includes() es un loop, ya que recorre los elementos de guests // guests es igual en cada iteración de este forEach user.isGuest = guests.includes(user.id) }) // Eficiente const guestsSet = new Set(guests) users.forEach(user => { // guestsSet.has() no necesita recorrer todos los elementos de gestsSet para saber si el elemento existe user.isGuest = guestsSet.has(user.id) }) ``` - E.g. Identificar proyectos a los que un usuario está invitado ```javascript // projects: object[]. Arreglo de proyectos con id:string y guests:string[] // userId: string. Id del usuario que se está consultando const projectIdsInvited = [] projects.forEach(({ id: projectId, guests }) => { // guests.includes() es un loop, ya que recorre los elementos de guests // guests es distinto en cada iteración de este forEach if (guests.includes(userId)) { projectIdsInvited.push(projectId) } }) ``` - Estas estructuras auxiliares son candidatos a memoizar (useMemo). # Estándares de Tests - [ ] Un `describe` por acción/función y crear tantos `describe` ó `tests` sean necesarios adentro para corroborar la funcioinalidad. - [ ] Revisar el coverage detallado en 'equip\coverage\lcov-report\index.html' para testear todas las líneas de código intervenidas. - [ ] Si hay rechazos pero su arreglo requiere de menos de 20 minutos y no afecta código que se ejecuta en producción, no es necesario rechazar, pero el revisor tiene que esperar al arreglo antes de entregar su compromiso de revisión. - [ ] No repetir valores que puedan ser distintos en el mismo objeto, como fechas o títulos. - E.g. No: `{ createdAt: 1, updatedAt: 1 }`. Sí: `{ createdAt: 1, updatedAt: 2 }` ## FAQ - ¿Qué mockear? - Sí mockear: - Funciones que usen a rethink directamente. E.g.: queries - No se mockea rethink porque no tenemos mocks que testeen la funcionalidad de la query. ```javascript import { sleep0 } from '../../../Lib3S' const pausedDays = [] const request = { id: requestId } const queries = { getPausedDays: jest.fn(() => sleep0(pausedDays)).mockName('getPausedDays'), getRequest: jest.fn(() => sleep0(request)).mockName('getRequest') } ``` - Date: Usar el mock de Date implementado por nosotros. Cuando usemos Jest v26, podremos mockear Date usando jest ```javascript const RealDate = global.Date function fakeDate(now) { global.Date = class extends RealDate { constructor(...args) { super() if (args.length === 0) { return new RealDate(Dates3S.getDate(now)) } return new RealDate(...args) } static now() { return Dates3S.getTime(now) } } } // Resetear Date después de cada test afterEach(() => { global.Date = RealDate }) // Usar fakeDate(...) para que new Date retorne la fecha dada const now = new Date(2017, 0, 2, 0, 10, 20, 300) fakeDate(now) ``` - Lecturas/escrituras a archivos o a la consola. E.g.: basicLogger, console.log ```javascript var mockErrorLog var mockWarnLog jest.mock('../../basicLogger', () => fileName => { // Este if es necesario si se usa más de un basicLogger if (mockErrorLog === undefined) { mockErrorLog = jest.fn() mockWarnLog = jest.fn() } return { error: mockErrorLog, warn: mockWarnLog } }) let consoleLogSpy beforeAll(() => { consoleLogSpy = jest .spyOn(console, 'log') .mockImplementation(() => {}) .mockName('console.log') }) afterAll(() => { consoleLogSpy.mockRestore() }) beforeEach(() => { consoleLogSpy.mockClear() }) ``` - Funciones que se conecten a internet. E.g. sockets ```javascript jest.mock('../sockets.js', () => ({ sendUpdate: jest.fn(() => {}), disconnectToken: jest.fn(() => {}) })) ``` - Funciones que retornen resultados aleatorios. E.g. uuid ```javascript jest.mock('uuid/v4', () => { let value = 0 return () => `uuid${value++}` }) ``` - Funciones que modifiquen variables globales. E.g. changeDate ```javascript jest.mock('../../../changeDate', () => jest.fn()) ``` - No mockear: - Funciones puras. - Funciones porque usen a queries. Se debe mockear queries. - Funciones porque usen a Date. Se debe mockear Date. - Funciones porque usen a un logger. Se debe mockear el logger. - ¿Cómo mockear una función que se importa desde otro módulo? ``` // file to be tested import {function1} from '../somewhere' // function to be tested export const getSomething = (prop1) => { function1() return 4 } ``` ``` // test file import {function1} from '../../somewhere' jest.mock('../../somewhere', ()=>({ function1: jest.fn(()=>{return 3}) })) ``` # Componentes ## Formato de changelog - Verbo y nombre del componente - Constructor: - Descripción de un cambio - State y stateHandlers: - Descripción de un cambio - Ej: Se agregó una variable llamada showNewRequirements que es modificada a través del handler toggleNewRequirements cuando el usuario hace click en el ícono de abrir. - Props: - Descripción de un cambio - EventHandlers - Descripción de un cambio - Funciones helpers - Descripción de un cambio - Render - Descripción de un cambio - Clases de Css - Descripción de un cambio - Condiciones - Descripción de un cambio - Hooks - Descripción de un cambio - Funciones del ciclo de vida - Descripción de un cambio - Video tutorial - Descripción del cambio - Acciones de redux - Descripción del cambio ## Estándares a revisar - En el changelog - [ ] Se enumeran todos los componentes editados - [ ] Se describe el resultado deseado de los componentes - [ ] Se describen todos los cambios realizados en sus respectivas "secciones" - [ ] ¿Se cumplen los criterios de comentarios? - [ ] ¿Están documentados los escenarios? (Falta definir cómo) - Se crea un componente - [ ] ¿Se evaluó la posibilidad de utilizar un componente existente? - [ ] Se incluyeron todos los elementos (ej de dejar jsx en el componente padre que podrían incluirse en el componente nuevo) - Props - [ ] Los props están documentados con jsdoc. No es necesario explicar los autoexplicativos (title, readOnly, maxLength), solo definir su nombre y su tipo. - PropTypes: - Si recibo un array, pero solo lo entrego a mis hijos, defino el tipo de sus elementos. - Si recibo un objeto, pero solo lo entrego a mis hijos, lo defino como objeto - Si recibo algo que uso, debe estar definido las key que se usan (Shape) - Es requerido si el componente falla cuando no recibe esa prop. - State y stateHandlers - [ ] Los atributos del state cumplen con los criterios para pertenecer al estado - [ ] Las variables están comentadas - [ ] Las variables cumplen con la notación - stateHandlers: - [ ] Su nombre debe contener el nombre de la variable del estado que modifica. - [ ] Si la modificación del state depende de su valor anterior, utilizar la versión del setState() que recibe una función en vez de un objeto. [Documentación](https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous) - [ ] Nunca mutar el estado de los componentes. - [ ] Componente Sin estado - [ ] Si es un componente nuevo, debe ser funcional, si necesita estado, utiliza hooks. - [ ] si un componente no usa state, debe ser refactorizado a funcional - [ ] Si el componente era funcional y con el cambio que se esta aplicando se necesita agregar un state, hay que utilizar hooks. [Video ejemplo hooks](https://drive.google.com/file/d/10uoNTXV4qFXSde5hy8cUj_i4Snfb8kLj/view) [repo](https://github.com/ljle/hooks-todo-exampledocke) - EventHandlers - [ ] Se debe documentar cuando se ejecuta la acción y cuál es su consecuencia. - Render - [ ] No hay lógica extraible en funciones helpers - Funciones helpers - [ ] Está documentada en jsdoc - [ ] Está comentada - [ ] Tiene tests unitarios - [ ] Están definidas fuera del componente en un archivo llamado utils.js - [ ] Si es una función de condiciones, se deben comentar todos sus escenarios. - Tests - [ ] El componente tiene [storyshots](https://drive.google.com/file/d/1PTXNJSOrnXd9QB4MYsg-cSsU2qgFPDr5/view?usp=sharing) - [ ] Todas las funciones helpers tienen tests - ReactHooks - Cuando se utiliza el hook [useMemo](https://reactjs.org/docs/hooks-reference.html#usememo), todas las variables dentro de la función a memoizar deben estar en el array. - Clases de Css - [ ] Notación de nombres de clases - [ ] Considera los distintos tamaños de pantalla/resolución - [ ] En mobile: 360px x 660px (viewport) - [ ] En desktop: 1280px x 575px (viewport) - [ ] Si eliminó código, se eliminaron las clases que no se van a utilizar - [ ] Iconos: Deben ser importados import * as Icons from @alliende/3styles/lib/icons y luego lo utilizas desde el objeto Icons - Condiciones - [ ] En los componentes funcionales no hay que utilizar defaultProps [Razón](https://stackoverflow.com/questions/47774695/react-functional-component-default-props-vs-default-parameters/56443098#56443098) - General - [ ] Documentación y comentarios en inglés - [ ] La lógica utilizada para formatear data debe ir en una función helper, a menos de que sea lo único realizado en la función que lo ejecuta. - [ ] El nombre de un componente [HOC]() debe tener el prefijo with cuando es llamado por el componente a envolver. ## FAQ - [Criterios con respecto a las listas](https://hackmd.io/@moqOc1m4TV6zGd_vaZ-2Sg/SkPe-R2yw) - Nombre de los indicadores - Si tienes el problema de que no se encuentra un componente recién creado, asegurate de que el nombre del componente no es igual a algún archivo de gitignore - Colores de equip: - Verde #66CC33 - Naranjo: #FF9500 - Azul: #00539F - Gris oscuro: #9B9B9B - Gris claro: #D8D8D8 - Rojo: #EF2A16 - Blanco: #FFFFFF - ¿Para qué sirve el constructor en el componente? Función que permite crear e inicializar el componente, debe definir el estado incial del componente utilizando los props. - ¿Creo un nuvo componente o modifico uno que ya existe? - ¿Necesito estado en este componente? - ¿Qué tipos de componente existen? - ¿Cual es la notación para definir el nombre de una variable en el estado? - Si la variable se utiliza para mostrar una sección de jsx, que sunombre empiece con show - Si es una variable booleana, comenzar su nombre con is ó can. - Si tenemos una variable del state que se llama igual que una prop, en el reder utilizamos la variable del state agregandole una S al final `{implementationProjectedTime: implementationProjectedTimeS} = state` - ¿Cuál es la notación para definir el nombre de los eventHandlers? - Si el eventHandler modifica una variable del estado, que parte del nombre de esa variable esté en el nombre - Si modifica un valor boleano, que comience con toogle - ¿Qué criterios debo aplicar para definir los props de un componente? - Entregar a los componentes solamente los props "minimos" para que este funcione correctamente, y no darle más contexto del necesario. - ¿Cuándo agrego algo al state del componente? - ¿Cuales son las funciones del ciclo de vida de un componente? Son métodos del componente que permiten interactuar con el en sus distintas etapas. [Api del Componente](https://reactjs.org/docs/react-component.html) - ¿Qué criterios debo tomar en cuenta para comentar el código de un componente? Debes comentar el "por qué" del código y las secciones donde se agrupa lógica. - ¿Qué lógica debe ir en el render? - Solo consultar una o dos variables, cualquier cosa extra debe ser una función helper o una nueva variable. # Acciones web ## Formato de changelog - Nombre de la Acción: Qué implicancia tiene para el usuario - Reducers implementados - Nombre reducer: descripción de la transformación - Verbo y Acción de confirmación: - Nombre reducer: descripción de la transformación - Verbo y Acción derivada: - Nombre reducer: descripción de la transformación ## Estándares a revisar - Changelog - [ ] Se incluyen todas las acciones, sus verbos, y la descripción de sus reducers - Agregar una nueva acción - [ ] Se agregó al archivo redux/actionNames.js - [ ] Se agregó al archivo redux/confirmationActionTypes y redux/derivatedActionTypes - En el reducer - [ ] ¿Cómo se escogen los reducers a editar? Un mismo cambio se puede hacer solo en agenda o en agenda y en requests. - [ ] ¿Cuáles son los criterios para comentar y documentar? - [ ] Debe tener tests que demuestren que el reducer es puro (ver librería rec) - [ ] Utilizar una sola vez la función update() de inmutability-helper, a menos de que se deba actualizar en un loop - [ ] Corroborar que el reducer de cada acción es homologo a la función actionReducer del servidor, para evitar problemas de actualización ("... con f5 cambia") - General - [ ] Documentación y comentarios en inglés - [ ] Evitar utilizar flags en los atributos de las acciones, es mejor crear acciones distintas, a menos de que: el payload de la acción sea simple (no más de 5 atributos), las acciones son del mismo modelo y no hay otras flags. - Modelos: - Cada vez que se actualiza uno de los siguientes modelos: Tareas, compromisos, pedidos o subtareas (del reducer de agenda), hay que modificar su función create en su librería respectiva (dentro de la carpeta libs). Y si queremos agregar un atributo a un modelo en una acción que antes no lo hacía, utilizar su función update de su librería respectiva. - EJ1: Si quiero agregar el atributo color a el modelo task, debo asegurarme que la función `createTask` de `libs/priorities/index.js` incorpora ese atributo. - EJ2: Si creo una nueva acción llamada `CHANGE_TASK_COLOR` debo asegurarme de que el reducer de agenda se utilice la función `updatePriority` de `libs/priorities/index.js` para modificar el modelo tarea. - Acciones GET_: Las acciones de carga gradual deben eliminar la información previa que han cargado en otras ocaciones y cargar la nueva, con el objetivo de disminuir la lógica de elminar elementos del state que cumplan con alguna condición, simplemente se elimina todo lo que carga esa llamada y se vuelve a cargar toda la info, ya que estás queries contienen toda la información en su ultima versión de la BD. ## FAQ # Acciones api ## Formato de changelog - Nombre de la acción: (Si es una acción nueva. Describir los cambios en un parrafo con los subdominios y como se ejecuta.) - isInvalidSchema - getDocs - Descripción - getActionError - Descripción - actionReducer - Descripción - genDerivatedAction - Nombre de la acción: para quién y cuál es su consecuencia - Nombre de la acción: Modificación realizada - genConfirmAction - Consecuencia ## Estándares a revisar - Changelog - [ ] Nombró todas las ediciones / creaciones /eliminaciones de las acciones - Acciones - [ ] Acción generadora, actionTypes y actionNames - [ ] Esquema actualizado de la acción - [ ] Esquema tiene tests de aprobación y negación - [ ] Archivos que utiliza la acción - [ ] GetActionErrors - [ ] No confiar en la info que trae en el payload, de ser posible, corroborar esta con los documentos de la BD. - [ ] Se debe corroborar que la info que te envían solo es posible de generar en la ui. - [ ] GetDocs - [ ] Incluir en las llaves del objeto de los documentos un array u objeto vacío cuando el documento no existan. En vez de eliminar la llave.[Explicación](https://drive.google.com/file/d/1JQ-BaGQ5yNWLBsA7pF9BeZlC_ugy9r6-/view) - [ ] Asegurarse de que se utiliza el pluck para no incluir atributos innecesarios que además son relativamente grandes. Ojo sobretodo con getUser, que muchas veces se trae solo para conocer el leaderId, pero en cambio se trae completo. - [ ] actionReducer - [ ] Si estoy creando un objeto le agrego todos los atributos del modelo. - Tests - Cada test debe reiniciar los mocks que va a utilizar si es que el mock no lo definió el test. - No se puede modificar la respuesta del mock dentro de un test. En vez de eso debes crear un nuevo mock dentro test o fuera de él si es que otros tests lo requieren. - No se puede modificar variables definidas fuera del test. - Cada test solo puede llamar 1 sola vez a lo que se está testeando. - Todas las funciones mocks que representan funciones asincronas, deben ser asincronas. ```javascript // ejemplo funciones asycronas (pato) /** * Asynchronous function that lets the event loop continue before resolving. * Can be used to mock async functions. * * @see {@link https://www.youtube.com/watch?v=8aGhZQkoFbQ|What the heck is the event loop anyway? | Philip Roberts | JSConf EU} For more info about the event loop * * @param {any} resolveTo The returned Promise resolves to this * @returns {Promise} Resolves to the given parameter */ function sleep0(resolveTo) { return new Promise((resolve, reject) => { // This pushes the resolve to the event loop setTimeout(() => resolve(resolveTo), 0) }) } // query que no retorna valores const query1 = jest.fn(sleep0) // query que retorna valores const query2 = jest.fn((x) => sleep0(x)) ``` - Siempre debemos testear el resultado de la función y los efectos secundarios de lo que tengan. - Recorrer al menos 1 vez todos las "branches" del actionReducer. - (Criterio) Testeo de argumentos: Se debe testear un caso normal y los nombrados abajo, siempre y cuando haga sentido. Testear los valores que se castean. - Number: 0, negativos, positivos decimales. - String: '', 1 carácter, string tiene números - Array: Vacío, no Vacío - null - undefined - Crear la acción a través del actionGenerator. No definir a mano como objetos. - Llamar a lo que se está testando dentro de un test y no de un describe. - (criterio) Si un objeto es utilizado en varias acciones, lo defino en /mocks/testData.js - Si necesito un objeto en una sola acción, lo defino en el describe - Si necesito un objeto en una solo test, lo defino en el test. - Definir el nombre de la variable basandose en los atributos del objeto - Si no sé que esperar de un expect con el título actual del test (o hay varios expect) agregar un comentario. - El baseDocs debe ser por acción, y nunca contener keys a las que esa acción no tenga acceso. - (criterio) Solo modificar a través de update las variables, a menos de que sean muy pequeños. EJ: newCommitment, newrRquest - General - [ ] Documentación y comentarios en inglés - [ ] Corroborar que el actionReducer de cada acción es homologo a los reducers de la aplicación, para evitar problemas de actualización ("... con f5 cambia") - [ ] Evitar utilizar flags en los atributos de las acciones, es mejor crear acciones distintas, a menos de que: el payload de la acción sea simple (no más de 5 atributos), las acciones son del mismo modelo y no hay otras flags. ## FAQ # Modelos en la BD ## Formato de changelog - Verbo, tabla y atributo a modificar. Razón para modificarla. ## Estándares a revisar - [ ] Asegurate de que incluyes, de ser necesario, la modificación del [appstate](https://hackmd.io/BNcUd5fsQhmEBTfVLyTelg?both#Modelos-en-el-AppState) - [ ] ¿Se agregaron tests corroborando los atributos que se envian a la app? [Video Lucho]() - General - [ ] Documentación y comentarios en inglés - [ ] Una misma tabla en la BD no puede considera a la vez data que se acumula y data que se modifica (por ejemplo si tengo un documento que guarda la velocidad actual de los autos, no puede incorporar además la velocidades anteriores) La única excepción es si esos valores son muy pocos y se guardan en un array, por ejemplo expiredDates (fechas previas de vencimiento de un pedido). ## FAQ - ¿Cómo modifico la estructura o cambio valores de la BD? - ¿Puedo dejar un atributo en `undefined` cuando a ese objeto no le afecte ese atributo? No, sino lo afecta se le asigna el valor de `null`, queremos que todos nuestros modelos tengan los mismos atributos para disminuir posibles erros con variables `undefined`. # Modelos en el AppState ## Formato del changelog - Dominio.keys.key afectada: Tipo (objeto, array de objetos, string, etc...) [GETSTATE ó GET_nuevaAccion]. Razón para modificarlo ## Estándares a revisar - [ ] Proponer una nueva [versión del appstate](https://hackmd.io/TqYSuZfMQ6qZxFgmWiPJ9A?view) - [ ] Corroborar que la información no se duplica la información del appstate (no está en otra parte del appstate) - [ ] Si defino una acción para buscar data debe comenzar con el prefijo GET_ - [ ] La acción GET_ debe poner como true su variable de loading respectiva, para que el componente sepa si puede mostrar o no la información. - [ ] La acción de confirmación C_GET_... debe eliminar la data que existía anteriormente y cargar la nueva información (ya que estas queries deben contener toda la información, ya que no considera reutilizar info anterior) y cambiar el loading respectivo a false - [ ] ¿Se agregaron tests corroborando los atributos que se envian a la app desde el getState? - [ ] Si una parte del estado se utiliza desde dos páginas distintas, debe estar accesible en ambas. ## FAQ - **¿Qué es el appstate?** El appstate el objeto que contiene toda la información a utilizar en nuestra aplicación. Parte de esa información se carga en la primera carga llamada getstate (ver en queries getState()), otras partes de esa información se cargan con acciones particulares ejecutadas por el usuario. - **¿Cuál es el criterio para saber si una parte del appstate se debiese cargar en el getState o se debiese crear una acción particular para cargarla?** Si la parte del appstate en cuestión se visualiza sin que el cliente haga click en en alguna parte de la aplicación (a primera vista) debiese cargarse en la query del getState. Por el contrario, si la información solo se muestra después de que el cliente hizo un click, esa información debe cargarse en ese gesto. Ejemplo: En la pantalla de colaboradores, para ver la planificación y lo realizado hay que hacer click en el avión de los reportes directos. Entonces, lo correcto sería que al hacer click en el avión la acción de confirmación graiga de vuelta la información necesaria. En este caso, la planificación y lo realizado o los compromisos activos de ese usuario. - **¿Cuándo hay que actualizar los modelos en tiempo real (acciones derivadas)?** Los modelos deben actualizarse en tiempo real cuando cumplen con el criterio de que "están a primera vista". Nuestro método para actualizar en tiempo real son las acciones derivadas, que se crean para actualizar a los clientes, este método se utilizará solo para los modelos que están a simple vista. Por ejemplo, si un usuario le agrega duración a una subtarea de un compromiso que pertenece a un proyecto, ya no hay que enviarle la derivada al creador de ese proyecto, ya que esa información se descargará cuando el haga click en ver los detalles de ese proyecto. > []Lo anterior es equivalente a decir que se deben crear acciones derivadas usando el mismo criterio del Getstate? - **¿Cuál es el proceso para modificar la estructura del Appstate?** 1. Modificar la versión del appstate en [Documento](https://hackmd.io/TqYSuZfMQ6qZxFgmWiPJ9A?view) 2a. Si la información esta disponible a primera vista, se debe agregar la información en la query getState 2b. Si la información esta disponible después de una acción, se debe crear una nueva acción con el prefijo GET_, su acción debe definir en la variable `loading` de su dominio como `true`, para demostrar que se está cargando, y su confirmación debe traer la información y en su reducer agregarla al appstate y poner en false la variable. Hay que cumplir con todas las normas de las acciones (por ejemplo validarse, tener un getDocs, etc...) - ¿Cómo saber si está cargando o no la variable? Cada dominio debe tener una variable llamada loading que es un objeto donde sus keys son el nombre de la variable que está cargandose. Cuando se ejecuta la acción GET, debe poner true su respectiva variable, por ejemplo, si se dispatcha la acción `GET_ACTIVE_REQUESTS` la variable collaborators.loading.activeRequests se vuelve true, hasta que retorna la acción `C_GET_ACTIVE_REQUESTS` que guarda la nueva información y la pone en false. # Consecuencias del cron ## Formato del changelog - Nombre de consecuencia: Cuál es su consecuencia - Modificación a realizar ## Estándares a revisar - [ ] Es una función pura y está corroborado por l - [ ] Cuánto aumenta el tiempo de ejecución **@faltaImplementarCriterio** - [ ] Se testean unitariamente todos los escenarios - General - [ ] Documentación y comentarios en inglés ## FAQ # Indicadores ## Formato del changelog - Indicador - Explicar modificación ## Estándares a revisar - [ ] Se testean todos las posibilidades de los objetos de la ecuación. - General - [ ] Documentación y comentarios en inglés ## FAQ # Queries ## Formato del changelog - Nombre query: descripción de la query, entre paréntesis nombra las acción(es) que la ejecutan. ## Estándares a revisar - [ ] Se Utiliza index en vez de filter, a menos que los resultados que hay que filtrar no vayan a aumentar en el tiempo, o no tenga sentido el índice. - [ ] Evitar hacer más de una query por objetivo. (Si necesito traer todas las subtareas de los compromisos activos, hago una sola query) - [ ] Si necesito hacer más de una query y no deben ser consecutivas, utilizar [Promise.all](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) - [ ] La data que trae la query debe hacer sentido con la acción que se está ejecutando. Debe ser lo más pequeña posible o estar justificado su argumento para el tamaño. - [ ] No definir la query con atributos al estilo ...objeto, ya que se esconde los valores a ser ingresados. En vez, hacer destructuring y agregar de una en una, para ser explícitos. - [ ] Los tests corroboran que la query funciona correctamente para todos sus escenarios - [ ] Se corrobora cuántas veces se debe llamar a la query - [ ] Se corrobora con qué argumentos se llama a la query - [ ] Se corrobora qué retorna la query - [ ] Cuánto se demora en ejecutar la query **@faltaImplementarCriterioConServidorStaging** - [ ] Documentación en JSDOC de la query - [ ] Comentarios en inglés ## FAQ - [Video](https://drive.google.com/file/d/1quyC-yBNKKGVIRBop82-yKUL8UuJVlZF/view?usp=sharing) - **¿Qué es un índice?** Una estructura dentro de un documento de la base de datos que permite realizar queries de lectura más rápidamente. [Documentación](https://www.rethinkdb.com/docs/secondary-indexes/javascript/) La analogía de un índice es buscar una palabra en un diccionario, versis sin índice que sería revisar palabra por palabra si es equivalente a la que estoy buscando. - **¿Qué queries utilizo cuando tengo un índice disponible?** La query para utilizar índices primarios es get, y para usar índices secundarios es [getAll](https://www.rethinkdb.com/api/javascript/get_all/) No se puede encadenar la query getALl, si necesitas buscar un valor que depende de varios índices deberías crear un índice compuesto [Documentación](https://www.rethinkdb.com/docs/secondary-indexes/javascript/) ## Diseño de una query 1. Define qué información específica necesitas obtener, llama la menor cantidad de información posible e intenta obtener todos los datos con una sola query. 2. Crea la query en el dataExplorer 1. ¿Cuánto cambia el tiempo de la query si utilizas un índice? 2. Evitar utilizar map dentro de la query para obtener más datos, utiliza en vez [joins](https://www.rethinkdb.com/docs/table-joins/) 3. En el código 1. Minimizar la cantidad de queries 2. Si necesito hace más de una query wrapearlas en un Promise.all a menos de que se necesiten consecutivamente # Librerías internas ## Formato del changelog - Nombre de la librería a modificar - Modificación ## Estándares a revisar - [ ] La función se testea en todas sus posibilidades - [ ] Está documentada con jsdoc - [ ] No es redundante con otra función en la misma librería u otra librería - General - [ ] Documentación y comentarios en inglés ## FAQ # Librerías externas ## Formato del changelog - nombre de la librería, link a repositorio - Razón de por qué es necesaria. - Nombrar las otras alternativas que existen y que se desecharon. ## Estándares a revisar - [ ] La librería tiene menos dependencias que las alternativas. - [ ] La libería tiene más downloads que las alternativas. - [ ] El último bug de la libería fue resuelto hace menos tiempo que las alternativas. - [ ] El package-lock.json solo modificó las librerías que fueron incluidas o eliminó las que fueron eliminadas. - [ ] Si package-lock.json se actualizó, se debe explicar cada actualización. ## FAQ # Agregar archivos al servidor ## Formato del changelog - Nombre de carpeta que guardará los archivos. - Descripción de cómo y cuándo se guarda el archivo (nombrar rutas y funciones involucradas) - Descripción de cómo y cuándo se elimina el archivo (nombrar rutas y funciones involucradas) - Descripción de cómo y cuándo se edita el archivo (nombrar rutas y funciones involucradas) ## Estándares a revisar - [ ] Se corroboró en el servidor de staging ## FAQ # Rutas App ## Formato del changelog - Nombre de la ruta a incorporar en la aplicación - Descripción de la razón para agregarla ## Estándares a revisar - [ ] Se define la ruta en el archivo app.js, el componente que la recibe y la condición para poder ingresar. - [ ] Se define la ruta en el archivo bootstrap/prodServer.js ## FAQ - ¿Por qué hay que modificar prodServer.js? prodServer es el servidor de producción de la app, es el encargado de que cada vez que un cliente lo llame se le devuelva la aplicación. Si no se agrega la nueva ruta al servidor, solo se le retornará la aplicación pero se rederigirá a /agenda, tal cual haría cualquier url mal formada o que no existe. # Rutas API ## Formato del changelog - Nombre de la ruta a incorporar en el servidor - Descripción de la razón para ingresarla - Describir el proceso la función handler ## Estándares a revisar - [ ] Toda la data recibida debe ser validada por JOI a nivel de formato y si encuentra error, debe devolver un 422 - [ ] Siempre que sea posible, se debe corroborar la data recibida con la información de la BD siempre asumiendo que lo recibido es erroneo o malicioso - [ ] Debe incluir mensajes del logger para registrar lo que sucede - [ ] Debe considerar todos los errores posibles y responder los errores correctos - [ ] Se deben envolver todas las operaciones dentro de un try catch ## FAQ # Staging Todas las entregas deben ser ejecutadas en su respectivo servidor de staging para que el emisor del pedido pueda corroborar todos los escenarios necesarios. ## FAQ - [¿Qué es un servidor de staging?](https://drive.google.com/file/d/16WsJtPk8xTHyWiaeeiqoo60WvnqI9IDv/view?usp=sharing) - [¿Cómo subo un nuevo cambio a staging?](https://drive.google.com/file/d/1U9_24HXvo0RHnEP39czFHSkDhaT6pTdn/view?usp=sharing) - [¿Cómo veo los logs de la api? ¿Cómo descargo y reinicio la BD?](https://drive.google.com/file/d/1htT3isTOdZy2Tih5uE8AqfsYpd6m1dyz/view?usp=sharing) - ¿Debo crear una branch local con el nombre del tag creado? No, se puede usar la misma rama que se esta trabajando para la entrega. Lo que si se debe tomar en cuenta es hacer el `push origin nombre_del_tag` - Como ejecuto el cron en local? - En la api se debe actualizar el comando `start` en el package.json añadiento la variable de entorno FAKE_DATE=true, quedando algo como: `"start": "FAKE_DATE=true NODE_ENV=develop API_PORT=2707 API_URL=http://localhost:2707 nodemon app.js --exec babel-node --presets env,stage-2",` - En la web actualizar el comando `start` del package.json y añadir la variable de entorno TESTING=true, quedando algo como: `"start": "TESTING=true webpack-dev-server --config webpack.dev.js --progress"` Nota: solo funciona la herramienta de correr la fecha, la de actualizar o guardar db no, usar la db disponible en este [link](https://demo.equipco.app/k43jn543jk5nINI63vBHJYhvjv67253hvjV62TVV27263Ljl723/rethinkdb_dump.tar.gz) - Como se la fecha inicial del servidor al usar esta ejecucion del cron en local? - en la tabla version muestra la ultima fecha en que se ejecuto el cron en `midnightsRanAt` - ¿Qué es y cómo funciona testDBManager? - TestDBManager es un servicio que recibe las acciones de reiniciar la base de datos y guardar la BD. - Para ejecutarlas, el servicio debe utilizar la consola y gestionar los contenedores - Es por eso que creamos un nuevo servicio, fuera de docker, que permitiera gestionarlo. - TestDBManager es gestionado por PM2 - pm2 status - pm2 start dist/index.js --name testDBManagerX La X anterior depende de la instancia que sea el testDBManager. Asegurarse de instalar y correr run build. - pm2 logs, para escuchar los mensajes - Hay que definir un nuevo servidor virtual en nginx para recibir las llamadas. ``` location /restart { proxy_pass http://localhost:4343; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } location /save { proxy_pass http://localhost:4343; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } ``` - ¿Por qué testDBManager es un servicio distinto de equip? - Porque para reiniciar la BD debíamos eliminar el contenedor de la BD actual, crear otro y cargar el loadDB, y para guardar la BD debíamos enviarle la ejecución a la rethink desde dentro de su contenedor BD. Para poder ejecutar la gestión de un contenedor, debemos estar "fuera de docker" y no dentro de un contenedor en particular. Por lo que entonces creamos un nuevo servicio. ## Proceso 1. Commitear el repositorio 2. Agregarle el tag `git tag 2019-MM-DD.HH.MM.desarrollador` 3. Enviar el tag a github `git push origin 2019-MM-DD.HH.MM.desarrollador` 4. Descargar la BD actual de Demo: `https://demo.equipco.app/k43jn543jk5nINI63vBHJYhvjv67253hvjV62TVV27263Ljl723/rethinkdb_dump.tar.gz` 5. Subir la BD a mi servicio de equip: `scp /direccion/BD/en mi/local/rethinkdb_dump.tar.gz root@desarrollador:/home2/db_data/loadDB` *home1 ó home 7. Modificar el tag de las imagenes del servicio - `ssh root@id_de_tu_servidor_de_staging` - Escoger un directorio para levantar el proceso. - Para levantar el proceso están disponibles 3 directorios (solo puedes usar dos en paralelo), en cualquiera de ellos puedes modificar el docker-compose, modificarle las imagenes y hacer un docker-compose up. - `cd /home` ó `cd /home1` ó `cd /home2` - `nano docker-compose.yml` - Modificar el nombre de los tags a ejecutar - `docker-compose up -d`