├── .gitignore ├── 01 ├── src │ ├── create_todos_db.sql │ ├── .env.template │ ├── Dockerfile.todos_db │ ├── .env │ ├── .env.test │ ├── .prettierrc │ ├── src │ │ ├── dals │ │ │ ├── todos │ │ │ │ ├── todo.entity.ts │ │ │ │ ├── todo.contract.dal.ts │ │ │ │ └── todo.dal.ts │ │ │ └── dataAccess.ts │ │ ├── models │ │ │ └── todo.model.ts │ │ ├── app.spec.ts │ │ ├── app.ts │ │ └── app.test.ts │ ├── nodemon.json │ ├── jest.config.js │ ├── jest.e2e.config.js │ ├── Dockerfile │ ├── .dockerignore │ ├── migrations │ │ ├── 20201202120018_create_todos_table.js │ │ └── 20201202120123_update_todos_table.js │ ├── tsconfig.json │ ├── data │ │ └── seeds │ │ │ └── todos.js │ ├── package.json │ ├── knexfile.js │ └── README.md ├── demo2 │ ├── 2.1 │ │ └── Jenkinsfile │ ├── 2.2 │ │ └── Jenkinsfile │ └── 2.3 │ │ └── Jenkinsfile ├── demo1 │ ├── 1.1 │ │ └── Jenkinsfile │ ├── 1.2 │ │ └── Jenkinsfile │ └── 1.3 │ │ └── Jenkinsfile └── demo3 │ └── Jenkinsfile ├── 02 ├── src │ ├── create_todos_db.sql │ ├── .env.template │ ├── Dockerfile.todos_db │ ├── .env │ ├── .env.test │ ├── .prettierrc │ ├── src │ │ ├── dals │ │ │ ├── todos │ │ │ │ ├── todo.entity.ts │ │ │ │ ├── todo.contract.dal.ts │ │ │ │ └── todo.dal.ts │ │ │ └── dataAccess.ts │ │ ├── models │ │ │ └── todo.model.ts │ │ ├── app.spec.ts │ │ ├── app.ts │ │ └── app.test.ts │ ├── nodemon.json │ ├── jest.config.js │ ├── jest.e2e.config.js │ ├── Dockerfile │ ├── .dockerignore │ ├── migrations │ │ ├── 20201202120018_create_todos_table.js │ │ └── 20201202120123_update_todos_table.js │ ├── tsconfig.json │ ├── data │ │ └── seeds │ │ │ └── todos.js │ ├── package.json │ ├── knexfile.js │ └── README.md ├── Dockerfile.node ├── Dockerfile └── demo1 │ ├── 1.2 │ ├── Jenkinsfile │ └── Jenkinsfile.fixed │ ├── 1.3 │ └── Jenkinsfile │ └── 1.1 │ └── Jenkinsfile ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /01/src/create_todos_db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE todos_db; -------------------------------------------------------------------------------- /02/src/create_todos_db.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE todos_db; -------------------------------------------------------------------------------- /02/Dockerfile.node: -------------------------------------------------------------------------------- 1 | FROM node:alpine3.12 as builder 2 | 3 | ENV LEMONCODE_VAR=lemon -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bootcamp-jenkins-demo 2 | An independent repository to expose Jenkins demos 3 | -------------------------------------------------------------------------------- /01/src/.env.template: -------------------------------------------------------------------------------- 1 | DB_HOST= 2 | DB_USER= 3 | DB_PASSWORD= 4 | DB_PORT= 5 | DB_NAME= 6 | DB_VERSION= -------------------------------------------------------------------------------- /02/src/.env.template: -------------------------------------------------------------------------------- 1 | DB_HOST= 2 | DB_USER= 3 | DB_PASSWORD= 4 | DB_PORT= 5 | DB_NAME= 6 | DB_VERSION= -------------------------------------------------------------------------------- /01/src/Dockerfile.todos_db: -------------------------------------------------------------------------------- 1 | FROM postgres:10.4 2 | 3 | COPY ./create_todos_db.sql /docker-entrypoint-initdb.d -------------------------------------------------------------------------------- /02/src/Dockerfile.todos_db: -------------------------------------------------------------------------------- 1 | FROM postgres:10.4 2 | 3 | COPY ./create_todos_db.sql /docker-entrypoint-initdb.d -------------------------------------------------------------------------------- /01/src/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=develop 2 | PORT=3000 3 | DB_HOST=localhost 4 | DB_USER=postgres 5 | DB_PASSWORD=postgres 6 | DB_PORT=5432 7 | DB_NAME=todos_db 8 | DB_VERSION=10.4 -------------------------------------------------------------------------------- /02/src/.env: -------------------------------------------------------------------------------- 1 | NODE_ENV=develop 2 | PORT=3000 3 | DB_HOST=localhost 4 | DB_USER=postgres 5 | DB_PASSWORD=postgres 6 | DB_PORT=5432 7 | DB_NAME=todos_db 8 | DB_VERSION=10.4 -------------------------------------------------------------------------------- /01/src/.env.test: -------------------------------------------------------------------------------- 1 | NODE_ENV=test 2 | PORT=3001 3 | DB_HOST=localhost 4 | DB_USER=postgres 5 | DB_PASSWORD=postgres 6 | DB_PORT=5432 7 | DB_NAME=todos_db 8 | DB_VERSION=10.4 -------------------------------------------------------------------------------- /02/src/.env.test: -------------------------------------------------------------------------------- 1 | NODE_ENV=test 2 | PORT=3001 3 | DB_HOST=localhost 4 | DB_USER=postgres 5 | DB_PASSWORD=postgres 6 | DB_PORT=5432 7 | DB_NAME=todos_db 8 | DB_VERSION=10.4 -------------------------------------------------------------------------------- /01/src/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "endOfLine": "lf", 5 | "trailingComma": "all", 6 | "singleQuote": true, 7 | "arrowParens": "always" 8 | } 9 | -------------------------------------------------------------------------------- /02/src/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 120, 3 | "tabWidth": 2, 4 | "endOfLine": "lf", 5 | "trailingComma": "all", 6 | "singleQuote": true, 7 | "arrowParens": "always" 8 | } 9 | -------------------------------------------------------------------------------- /01/src/src/dals/todos/todo.entity.ts: -------------------------------------------------------------------------------- 1 | export interface TodoEntity { 2 | id: number; 3 | title: string; 4 | completed: boolean; 5 | due_date?: string; 6 | order?: number; 7 | } -------------------------------------------------------------------------------- /02/src/src/dals/todos/todo.entity.ts: -------------------------------------------------------------------------------- 1 | export interface TodoEntity { 2 | id: number; 3 | title: string; 4 | completed: boolean; 5 | due_date?: string; 6 | order?: number; 7 | } -------------------------------------------------------------------------------- /01/src/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "src" 4 | ], 5 | "ext": "ts", 6 | "ignore": [ 7 | "src/**/*.spec.ts" 8 | ], 9 | "exec": "ts-node ./src/app.ts" 10 | } -------------------------------------------------------------------------------- /02/src/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": [ 3 | "src" 4 | ], 5 | "ext": "ts", 6 | "ignore": [ 7 | "src/**/*.spec.ts" 8 | ], 9 | "exec": "ts-node ./src/app.ts" 10 | } -------------------------------------------------------------------------------- /01/src/jest.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: '.env.test', 3 | }); 4 | 5 | module.exports = { 6 | testEnvironment: 'node', 7 | roots: ['/src'], 8 | testMatch: ['**/?(*.)+(spec).+(ts|js)'], 9 | transform: { 10 | '^.+\\.ts$': 'ts-jest', 11 | }, 12 | }; -------------------------------------------------------------------------------- /02/src/jest.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: '.env.test', 3 | }); 4 | 5 | module.exports = { 6 | testEnvironment: 'node', 7 | roots: ['/src'], 8 | testMatch: ['**/?(*.)+(spec).+(ts|js)'], 9 | transform: { 10 | '^.+\\.ts$': 'ts-jest', 11 | }, 12 | }; -------------------------------------------------------------------------------- /01/src/jest.e2e.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: '.env.test', 3 | }); 4 | 5 | module.exports = { 6 | testEnvironment: 'node', 7 | roots: ['/src'], 8 | testMatch: ['**/?(*.)+(test).+(ts|js)'], 9 | transform: { 10 | '^.+\\.ts$': 'ts-jest', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /02/src/jest.e2e.config.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config({ 2 | path: '.env.test', 3 | }); 4 | 5 | module.exports = { 6 | testEnvironment: 'node', 7 | roots: ['/src'], 8 | testMatch: ['**/?(*.)+(test).+(ts|js)'], 9 | transform: { 10 | '^.+\\.ts$': 'ts-jest', 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /01/demo2/2.1/Jenkinsfile: -------------------------------------------------------------------------------- 1 | library identifier: 'bootcamp-jenkins-library@main', 2 | retriever: modernSCM([$class: 'GitSCMSource', remote: 'https://github.com/Lemoncode/bootcamp-jenkins-library.git']) 3 | 4 | pipeline { 5 | agent any 6 | stages { 7 | stage('Audit tools') { 8 | steps { 9 | auditTools() 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /01/src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine3.12 as builder 2 | 3 | WORKDIR /build 4 | 5 | COPY . . 6 | 7 | RUN npm install 8 | 9 | RUN npm run build 10 | 11 | FROM node:alpine3.12 as application 12 | 13 | WORKDIR /opt/app 14 | 15 | COPY package.json . 16 | 17 | COPY package-lock.json . 18 | 19 | COPY --from=builder /build/app . 20 | 21 | RUN npm i --only=production 22 | 23 | CMD ["node", "app.js"] -------------------------------------------------------------------------------- /02/src/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine3.12 as builder 2 | 3 | WORKDIR /build 4 | 5 | COPY . . 6 | 7 | RUN npm install 8 | 9 | RUN npm run build 10 | 11 | FROM node:alpine3.12 as application 12 | 13 | WORKDIR /opt/app 14 | 15 | COPY package.json . 16 | 17 | COPY package-lock.json . 18 | 19 | COPY --from=builder /build/app . 20 | 21 | RUN npm i --only=production 22 | 23 | CMD ["node", "app.js"] -------------------------------------------------------------------------------- /02/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:alpine3.12 as builder 2 | 3 | WORKDIR /build 4 | 5 | COPY ./src . 6 | 7 | RUN npm install 8 | 9 | RUN npm run build 10 | 11 | FROM node:alpine3.12 as application 12 | 13 | WORKDIR /opt/app 14 | 15 | COPY ./src/package.json . 16 | 17 | COPY ./src/package-lock.json . 18 | 19 | COPY --from=builder /build/app . 20 | 21 | RUN npm i --only=production 22 | 23 | ENTRYPOINT ["node", "app.js"] -------------------------------------------------------------------------------- /01/demo2/2.2/Jenkinsfile: -------------------------------------------------------------------------------- 1 | library identifier: 'bootcamp-jenkins-library@main', 2 | retriever: modernSCM([$class: 'GitSCMSource', remote: 'https://github.com/Lemoncode/bootcamp-jenkins-library.git']) 3 | 4 | pipeline { 5 | agent any 6 | stages { 7 | stage('Audit tools') { 8 | steps { 9 | auditTools2 message: 'This is demo 2' 10 | } 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /01/src/.dockerignore: -------------------------------------------------------------------------------- 1 | # Local Devolpment 2 | nodemon.json 3 | 4 | # environment setup 5 | .env 6 | .env.test 7 | .env.template 8 | 9 | # Test files 10 | /**/*.spec.ts 11 | /**/*.test.ts 12 | 13 | # Dependencies 14 | node_modules/ 15 | 16 | # Local build directory 17 | app/ 18 | 19 | # Database setup 20 | data/ 21 | migrations/ 22 | knexfile.js 23 | create_todos_db.sql 24 | Dockerfile.todos_db 25 | 26 | # Guides 27 | README.md -------------------------------------------------------------------------------- /01/src/migrations/20201202120018_create_todos_table.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | exports.up = function(knex) { 4 | return knex.schema.createTable('todos', function(table) { 5 | table.increments('id'); 6 | table.string('title', 255).notNullable(); 7 | table.boolean('completed').notNullable(); 8 | }); 9 | }; 10 | 11 | exports.down = function(knex) { 12 | return knex.schema.dropTable('users'); 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /01/src/migrations/20201202120123_update_todos_table.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.table('todos', (t) => { 4 | t.datetime('due_date'); 5 | t.integer('order'); 6 | }); 7 | }; 8 | 9 | exports.down = function(knex) { 10 | return knex.schema.table('todos', (t) => { 11 | t.dropColumn('due_date'); 12 | t.dropColumn('order'); 13 | }); 14 | }; -------------------------------------------------------------------------------- /01/src/src/dals/dataAccess.ts: -------------------------------------------------------------------------------- 1 | import Knex, { PgConnectionConfig } from 'knex'; 2 | 3 | type ConnectionParams = PgConnectionConfig & { dbVersion: string }; 4 | 5 | export const startConnection = ({ dbVersion: version, ...connection }: ConnectionParams) => { 6 | try { 7 | return Knex({ 8 | client: 'pg', 9 | version, 10 | connection, 11 | }); 12 | } catch (error) { 13 | throw error; 14 | } 15 | }; -------------------------------------------------------------------------------- /02/src/.dockerignore: -------------------------------------------------------------------------------- 1 | # Local Devolpment 2 | nodemon.json 3 | 4 | # environment setup 5 | .env 6 | .env.test 7 | .env.template 8 | 9 | # Test files 10 | /**/*.spec.ts 11 | /**/*.test.ts 12 | 13 | # Dependencies 14 | node_modules/ 15 | 16 | # Local build directory 17 | app/ 18 | 19 | # Database setup 20 | data/ 21 | migrations/ 22 | knexfile.js 23 | create_todos_db.sql 24 | Dockerfile.todos_db 25 | 26 | # Guides 27 | README.md -------------------------------------------------------------------------------- /02/src/migrations/20201202120018_create_todos_table.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | exports.up = function(knex) { 4 | return knex.schema.createTable('todos', function(table) { 5 | table.increments('id'); 6 | table.string('title', 255).notNullable(); 7 | table.boolean('completed').notNullable(); 8 | }); 9 | }; 10 | 11 | exports.down = function(knex) { 12 | return knex.schema.dropTable('users'); 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /02/src/migrations/20201202120123_update_todos_table.js: -------------------------------------------------------------------------------- 1 | 2 | exports.up = function(knex) { 3 | return knex.schema.table('todos', (t) => { 4 | t.datetime('due_date'); 5 | t.integer('order'); 6 | }); 7 | }; 8 | 9 | exports.down = function(knex) { 10 | return knex.schema.table('todos', (t) => { 11 | t.dropColumn('due_date'); 12 | t.dropColumn('order'); 13 | }); 14 | }; -------------------------------------------------------------------------------- /02/src/src/dals/dataAccess.ts: -------------------------------------------------------------------------------- 1 | import Knex, { PgConnectionConfig } from 'knex'; 2 | 3 | type ConnectionParams = PgConnectionConfig & { dbVersion: string }; 4 | 5 | export const startConnection = ({ dbVersion: version, ...connection }: ConnectionParams) => { 6 | try { 7 | return Knex({ 8 | client: 'pg', 9 | version, 10 | connection, 11 | }); 12 | } catch (error) { 13 | throw error; 14 | } 15 | }; -------------------------------------------------------------------------------- /01/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "removeComments": false, 10 | "noImplicitAny": false, 11 | "outDir": "./app", 12 | "rootDir": "./src", 13 | "esModuleInterop": true 14 | }, 15 | "exclude": ["node_modules", "typings"] 16 | } 17 | -------------------------------------------------------------------------------- /02/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "sourceMap": true, 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "removeComments": false, 10 | "noImplicitAny": false, 11 | "outDir": "./app", 12 | "rootDir": "./src", 13 | "esModuleInterop": true 14 | }, 15 | "exclude": ["node_modules", "typings"] 16 | } 17 | -------------------------------------------------------------------------------- /01/src/data/seeds/todos.js: -------------------------------------------------------------------------------- 1 | 2 | exports.seed = function(knex) { 3 | // Deletes ALL existing entries 4 | return knex('todos').truncate() 5 | .then(function () { 6 | // Inserts seed entries 7 | return knex('todos').insert([ 8 | {id: 1, title: 'Learn GitHub Actions', completed: false}, 9 | {id: 2, title: 'Learn Groovy', completed: false}, 10 | {id: 3, title: 'Learn EKS', completed: false} 11 | ]); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /02/src/data/seeds/todos.js: -------------------------------------------------------------------------------- 1 | 2 | exports.seed = function(knex) { 3 | // Deletes ALL existing entries 4 | return knex('todos').truncate() 5 | .then(function () { 6 | // Inserts seed entries 7 | return knex('todos').insert([ 8 | {id: 1, title: 'Learn GitHub Actions', completed: false}, 9 | {id: 2, title: 'Learn Groovy', completed: false}, 10 | {id: 3, title: 'Learn EKS', completed: false} 11 | ]); 12 | }); 13 | }; 14 | -------------------------------------------------------------------------------- /01/src/src/dals/todos/todo.contract.dal.ts: -------------------------------------------------------------------------------- 1 | import { TodoEntity } from './todo.entity'; 2 | 3 | export interface TodoDAL { 4 | getTodos(): Promise; 5 | getTodoById(id: number): Promise; 6 | createTodo(todo: TodoEntity): Promise; 7 | updateTodo(id: number, todo: TodoEntity): Promise; 8 | resolveTodos(): Promise; 9 | deleteTodoById(id: number): Promise; 10 | } 11 | 12 | export type TodoDALFactory = (...args: any[]) => TodoDAL; -------------------------------------------------------------------------------- /02/src/src/dals/todos/todo.contract.dal.ts: -------------------------------------------------------------------------------- 1 | import { TodoEntity } from './todo.entity'; 2 | 3 | export interface TodoDAL { 4 | getTodos(): Promise; 5 | getTodoById(id: number): Promise; 6 | createTodo(todo: TodoEntity): Promise; 7 | updateTodo(id: number, todo: TodoEntity): Promise; 8 | resolveTodos(): Promise; 9 | deleteTodoById(id: number): Promise; 10 | } 11 | 12 | export type TodoDALFactory = (...args: any[]) => TodoDAL; -------------------------------------------------------------------------------- /02/demo1/1.2/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | dockerfile { 4 | dir '02' 5 | } 6 | } 7 | stages { 8 | stage('Verify') { 9 | steps { 10 | sh''' 11 | node --version 12 | npm version 13 | ''' 14 | } 15 | } 16 | stage('Build') { 17 | steps { 18 | sh 'docker build -t jaimesalas/jenkins-pipeline-demos:0.0.1 .' 19 | } 20 | } 21 | stage('Smoke Test') { 22 | steps { 23 | sh 'docker run jaimesalas/jenkins-pipeline-demos:0.0.1' 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /02/demo1/1.3/Jenkinsfile: -------------------------------------------------------------------------------- 1 | def image 2 | 3 | pipeline { 4 | agent any 5 | stages { 6 | stage('Build') { 7 | steps { 8 | script { 9 | image = docker.build("jaimesalas/jenkins-pipeline-demos:0.0.1", "--pull -f 02/Dockerfile 02") 10 | } 11 | } 12 | } 13 | stage('Smoke Test') { 14 | steps { 15 | script { 16 | container = image.run() 17 | container.stop() 18 | } 19 | } 20 | } 21 | stage('Push') { 22 | steps { 23 | script { 24 | withDockerRegistry([credentialsId: "docker-hub", url: ""]) { 25 | image.push() 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /01/src/src/models/todo.model.ts: -------------------------------------------------------------------------------- 1 | import { TodoEntity } from '../dals/todos/todo.entity'; 2 | 3 | export interface TodoModel { 4 | id: number; 5 | title: string; 6 | completed: boolean; 7 | dueDate: string; 8 | } 9 | 10 | export const mapTodoEntity = (todo: TodoEntity): TodoModel => { 11 | const t = { 12 | ...todo, 13 | dueDate: todo.due_date, 14 | }; 15 | const { due_date, ...todoModel } = t; 16 | return todoModel; 17 | }; 18 | 19 | export const mapTodoEntityCollection = (todos: TodoEntity[]): TodoModel[] => todos.map(mapTodoEntity); 20 | 21 | export const mapTodoModel = (todo: TodoModel): TodoEntity => { 22 | const t = { 23 | ...todo, 24 | due_date: todo.dueDate, 25 | }; 26 | const { dueDate, ...todoEntity } = t; 27 | return todoEntity; 28 | }; 29 | 30 | export const mapTodoModelCollection = (todos: TodoModel[]): TodoEntity[] => todos.map(mapTodoModel); 31 | -------------------------------------------------------------------------------- /02/src/src/models/todo.model.ts: -------------------------------------------------------------------------------- 1 | import { TodoEntity } from '../dals/todos/todo.entity'; 2 | 3 | export interface TodoModel { 4 | id: number; 5 | title: string; 6 | completed: boolean; 7 | dueDate: string; 8 | } 9 | 10 | export const mapTodoEntity = (todo: TodoEntity): TodoModel => { 11 | const t = { 12 | ...todo, 13 | dueDate: todo.due_date, 14 | }; 15 | const { due_date, ...todoModel } = t; 16 | return todoModel; 17 | }; 18 | 19 | export const mapTodoEntityCollection = (todos: TodoEntity[]): TodoModel[] => todos.map(mapTodoEntity); 20 | 21 | export const mapTodoModel = (todo: TodoModel): TodoEntity => { 22 | const t = { 23 | ...todo, 24 | due_date: todo.dueDate, 25 | }; 26 | const { dueDate, ...todoEntity } = t; 27 | return todoEntity; 28 | }; 29 | 30 | export const mapTodoModelCollection = (todos: TodoModel[]): TodoEntity[] => todos.map(mapTodoModel); 31 | -------------------------------------------------------------------------------- /02/demo1/1.1/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | docker { 4 | image 'node:alpine3.12' 5 | } 6 | } 7 | stages { 8 | stage('Verify') { 9 | steps { 10 | sh ''' 11 | node --version 12 | npm version 13 | ''' 14 | sh 'printenv' 15 | sh 'ls -l "$WORKSPACE"' 16 | } 17 | } 18 | stage('Build') { 19 | steps { 20 | dir("$WORKSPACE/02/src") { 21 | sh ''' 22 | npm install 23 | npm build 24 | ''' 25 | } 26 | } 27 | } 28 | stage('Unit Test') { 29 | steps { 30 | dir("$WORKSPACE/02/src") { 31 | sh 'npm test' 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /01/src/src/dals/todos/todo.dal.ts: -------------------------------------------------------------------------------- 1 | import Knex from 'knex'; 2 | import { TodoDALFactory } from './todo.contract.dal'; 3 | import { TodoEntity } from './todo.entity'; 4 | 5 | export const todoDALFactory: TodoDALFactory = (knex: Knex) => ({ 6 | getTodos() { 7 | return knex('todos').then((r) => r); 8 | }, 9 | getTodoById(id: number) { 10 | return knex('todos').where('id', id).first(); 11 | }, 12 | async createTodo(todo: TodoEntity) { 13 | return await knex('todos').insert(todo); 14 | }, 15 | async updateTodo(id: number, todo: TodoEntity) { 16 | return await knex('todos') 17 | .where('id', id) 18 | .update({ ...todo }); 19 | }, 20 | async resolveTodos() { 21 | return await knex('todos').update({ 22 | completed: true, 23 | }); 24 | }, 25 | async deleteTodoById(id: number) { 26 | return await knex('todos').where('id', id).del(); 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /02/src/src/dals/todos/todo.dal.ts: -------------------------------------------------------------------------------- 1 | import Knex from 'knex'; 2 | import { TodoDALFactory } from './todo.contract.dal'; 3 | import { TodoEntity } from './todo.entity'; 4 | 5 | export const todoDALFactory: TodoDALFactory = (knex: Knex) => ({ 6 | getTodos() { 7 | return knex('todos').then((r) => r); 8 | }, 9 | getTodoById(id: number) { 10 | return knex('todos').where('id', id).first(); 11 | }, 12 | async createTodo(todo: TodoEntity) { 13 | return await knex('todos').insert(todo); 14 | }, 15 | async updateTodo(id: number, todo: TodoEntity) { 16 | return await knex('todos') 17 | .where('id', id) 18 | .update({ ...todo }); 19 | }, 20 | async resolveTodos() { 21 | return await knex('todos').update({ 22 | completed: true, 23 | }); 24 | }, 25 | async deleteTodoById(id: number) { 26 | return await knex('todos').where('id', id).del(); 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /02/demo1/1.2/Jenkinsfile.fixed: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent { 3 | dockerfile { 4 | dir '02' 5 | filename 'Dockerfile.node' 6 | } 7 | } 8 | stages { 9 | stage('Verify') { 10 | steps { 11 | sh ''' 12 | node --version 13 | npm version 14 | ''' 15 | sh 'printenv' 16 | sh 'ls -l "$WORKSPACE"' 17 | } 18 | } 19 | stage('Build') { 20 | steps { 21 | dir("$WORKSPACE/02/src") { 22 | sh ''' 23 | npm install 24 | npm build 25 | ''' 26 | } 27 | } 28 | } 29 | stage('Unit Test') { 30 | steps { 31 | dir("$WORKSPACE/02/src") { 32 | sh 'npm test' 33 | } 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /01/demo1/1.1/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | environment { 4 | VERSION = sh([ script: 'cd ./01/src && npm run env | grep "npm_package_version"', returnStdout: true ]).trim() 5 | VERSION_RC = "rc.2" 6 | } 7 | stages { 8 | stage('Audit tools') { 9 | steps { 10 | sh ''' 11 | git version 12 | docker version 13 | node --version 14 | npm version 15 | ''' 16 | } 17 | } 18 | stage('Build') { 19 | steps { 20 | dir('./01/src') { 21 | echo "Building version ${VERSION} with suffix: ${VERSION_RC}" 22 | sh ''' 23 | npm install 24 | npm run build 25 | ''' 26 | } 27 | } 28 | } 29 | stage('Unit Test') { 30 | steps { 31 | dir('./01/src') { 32 | sh 'npm test' 33 | } 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Lemoncode 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /01/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon", 8 | "prebuild": "rimraf app", 9 | "build": "tsc", 10 | "pretest": "jest --clearCache", 11 | "test": "jest -c ./jest.config.js --detectOpenHandles --verbose -i", 12 | "test:watch": "jest -c ./jest.config.js --watch --verbose -i", 13 | "test:e2e": "jest -c ./jest.e2e.config.js --detectOpenHandles --verbose -i", 14 | "test:watch:e2e": "jest -c ./jest.e2e.config.js --watch --verbose" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC", 19 | "devDependencies": { 20 | "@types/jest": "^26.0.15", 21 | "@types/supertest": "^2.0.10", 22 | "jest": "^26.6.3", 23 | "nodemon": "^2.0.6", 24 | "rimraf": "^3.0.2", 25 | "supertest": "^6.0.1", 26 | "ts-jest": "^26.4.4", 27 | "ts-node": "^9.0.0", 28 | "typescript": "^4.1.2" 29 | }, 30 | "dependencies": { 31 | "body-parser": "^1.19.0", 32 | "dotenv": "^8.2.0", 33 | "express": "^4.17.1", 34 | "knex": "^0.21.12", 35 | "pg": "^8.5.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /02/src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "code", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon", 8 | "prebuild": "rimraf app", 9 | "build": "tsc", 10 | "pretest": "jest --clearCache", 11 | "test": "jest -c ./jest.config.js --detectOpenHandles --verbose -i", 12 | "test:watch": "jest -c ./jest.config.js --watch --verbose -i", 13 | "test:e2e": "jest -c ./jest.e2e.config.js --detectOpenHandles --verbose -i", 14 | "test:watch:e2e": "jest -c ./jest.e2e.config.js --watch --verbose" 15 | }, 16 | "keywords": [], 17 | "author": "", 18 | "license": "ISC", 19 | "devDependencies": { 20 | "@types/jest": "^26.0.15", 21 | "@types/supertest": "^2.0.10", 22 | "jest": "^26.6.3", 23 | "nodemon": "^2.0.6", 24 | "rimraf": "^3.0.2", 25 | "supertest": "^6.0.1", 26 | "ts-jest": "^26.4.4", 27 | "ts-node": "^9.0.0", 28 | "typescript": "^4.1.2" 29 | }, 30 | "dependencies": { 31 | "body-parser": "^1.19.0", 32 | "dotenv": "^8.2.0", 33 | "express": "^4.17.1", 34 | "knex": "^0.21.12", 35 | "pg": "^8.5.1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /01/src/knexfile.js: -------------------------------------------------------------------------------- 1 | // Update with your config settings. 2 | module.exports = { 3 | development: { 4 | client: 'pg', 5 | connection: { 6 | host: process.env.DB_HOST || 'localhost', 7 | database: process.env.DB_NAME || 'todos_db', 8 | user: process.env.DB_USER || 'postgres', 9 | password: process.env.DB_PASSWORD || 'postgres', 10 | dbVersion: process.env.DB_VERSION || '10.4', 11 | port: +process.env.DB_PORT || 5432, 12 | }, 13 | pool: { 14 | min: 2, 15 | max: 10, 16 | }, 17 | migrations: { 18 | tableName: 'migrations', 19 | }, 20 | seeds: { 21 | directory: './data/seeds', 22 | } 23 | }, 24 | 25 | production: { 26 | client: 'pg', 27 | connection: { 28 | host: process.env.DB_HOST , 29 | database: process.env.DB_NAME, 30 | user: process.env.DB_USER, 31 | password: process.env.DB_PASSWORD, 32 | dbVersion: process.env.DB_VERSION, 33 | port: +process.env.DB_PORT, 34 | }, 35 | pool: { 36 | min: 2, 37 | max: 10, 38 | }, 39 | migrations: { 40 | tableName: 'migrations', 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /02/src/knexfile.js: -------------------------------------------------------------------------------- 1 | // Update with your config settings. 2 | module.exports = { 3 | development: { 4 | client: 'pg', 5 | connection: { 6 | host: process.env.DB_HOST || 'localhost', 7 | database: process.env.DB_NAME || 'todos_db', 8 | user: process.env.DB_USER || 'postgres', 9 | password: process.env.DB_PASSWORD || 'postgres', 10 | dbVersion: process.env.DB_VERSION || '10.4', 11 | port: +process.env.DB_PORT || 5432, 12 | }, 13 | pool: { 14 | min: 2, 15 | max: 10, 16 | }, 17 | migrations: { 18 | tableName: 'migrations', 19 | }, 20 | seeds: { 21 | directory: './data/seeds', 22 | } 23 | }, 24 | 25 | production: { 26 | client: 'pg', 27 | connection: { 28 | host: process.env.DB_HOST , 29 | database: process.env.DB_NAME, 30 | user: process.env.DB_USER, 31 | password: process.env.DB_PASSWORD, 32 | dbVersion: process.env.DB_VERSION, 33 | port: +process.env.DB_PORT, 34 | }, 35 | pool: { 36 | min: 2, 37 | max: 10, 38 | }, 39 | migrations: { 40 | tableName: 'migrations', 41 | } 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /01/demo3/Jenkinsfile: -------------------------------------------------------------------------------- 1 | library identifier: 'jenkins-pipeline-demo-library@main', 2 | retriever: modernSCM([$class: 'GitSCMSource', remote: 'https://github.com/Lemoncode/bootcamp-jenkins-library.git']) 3 | 4 | pipeline { 5 | agent any 6 | parameters { 7 | booleanParam(name: 'RC', defaultValue: false, description: 'Is this a Release Candidate?') 8 | } 9 | environment { 10 | VERSION = sh([ script: 'cd ./01/src && npm run env | grep "npm_package_version"', returnStdout: true ]).trim() 11 | VERSION_RC = "rc.2" 12 | } 13 | stages { 14 | stage('Audit tools') { 15 | steps { 16 | auditTools() 17 | } 18 | } 19 | stage('Build') { 20 | environment { 21 | VERSION_SUFFIX = getVersionSuffix rcNumber: env.VERSION_RC, isRealeaseCandidate: params.RC 22 | } 23 | steps { 24 | dir('./01/src') { 25 | echo "Building version ${VERSION} with suffix: ${VERSION_SUFFIX}" 26 | sh ''' 27 | npm install 28 | npm run build 29 | ''' 30 | } 31 | } 32 | } 33 | stage('Unit Test') { 34 | steps { 35 | dir('./01/src') { 36 | sh 'npm test' 37 | } 38 | } 39 | } 40 | stage('Publish') { 41 | when { 42 | expression { return params.RC } 43 | } 44 | steps { 45 | archiveArtifacts('01/src/app/') 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /01/demo2/2.3/Jenkinsfile: -------------------------------------------------------------------------------- 1 | library identifier: 'jenkins-pipeline-demo-library@main', 2 | retriever: modernSCM([$class: 'GitSCMSource', remote: 'https://github.com/Lemoncode/bootcamp-jenkins-library.git']) 3 | 4 | pipeline { 5 | agent any 6 | parameters { 7 | booleanParam(name: 'RC', defaultValue: false, description: 'Is this a Release Candidate?') 8 | } 9 | environment { 10 | VERSION = sh([ script: 'cd ./01/src && npm run env | grep "npm_package_version"', returnStdout: true ]).trim() 11 | VERSION_RC = "rc.2" 12 | } 13 | stages { 14 | stage('Audit tools') { 15 | steps { 16 | auditTools() 17 | } 18 | } 19 | stage('Build') { 20 | environment { 21 | VERSION_SUFFIX = getVersionSuffix rcNumber: env.VERSION_RC, isRealeaseCandidate: params.RC 22 | } 23 | steps { 24 | dir('./01/src') { 25 | echo "Building version ${VERSION} with suffix: ${VERSION_SUFFIX}" 26 | sh ''' 27 | npm install 28 | npm run build 29 | ''' 30 | } 31 | } 32 | } 33 | stage('Unit Test') { 34 | steps { 35 | dir('./01/src') { 36 | sh 'npm test' 37 | } 38 | } 39 | } 40 | stage('Publish') { 41 | when { 42 | expression { return params.RC } 43 | } 44 | steps { 45 | archiveArtifacts('01/src/app/') 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /01/demo1/1.2/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | /*diff*/ 4 | parameters { 5 | booleanParam(name: 'RC', defaultValue: false, description: 'Is this a Release Candidate?') 6 | } 7 | /*diff*/ 8 | environment { 9 | VERSION = sh([ script: 'cd ./01/src && npm run env | grep "npm_package_version"', returnStdout: true ]).trim() 10 | VERSION_RC = "rc.2" 11 | } 12 | stages { 13 | stage('Audit tools') { 14 | steps { 15 | sh ''' 16 | git version 17 | docker version 18 | node --version 19 | npm version 20 | ''' 21 | } 22 | } 23 | stage('Build') { 24 | /*diff*/ 25 | environment { 26 | VERSION_SUFFIX = "${sh(script:'if [ "${RC}" == "false" ] ; then echo -n "${VERSION_RC}+ci.${BUILD_NUMBER}"; else echo -n "${VERSION_RC}"; fi', returnStdout: true)}" 27 | } 28 | /*diff*/ 29 | steps { 30 | dir('./01/src') { 31 | // echo "Building version ${VERSION} with suffix: ${VERSION_RC}" 32 | echo "Building version ${VERSION} with suffix: ${VERSION_SUFFIX}" 33 | sh ''' 34 | npm install 35 | npm run build 36 | ''' 37 | } 38 | } 39 | } 40 | stage('Unit Test') { 41 | steps { 42 | dir('./01/src') { 43 | sh 'npm test' 44 | } 45 | } 46 | } 47 | /*diff*/ 48 | stage('Publish') { 49 | when { 50 | expression { return params.RC } 51 | } 52 | steps { 53 | archiveArtifacts('01/src/app/') 54 | } 55 | } 56 | /*diff*/ 57 | } 58 | } -------------------------------------------------------------------------------- /01/demo1/1.3/Jenkinsfile: -------------------------------------------------------------------------------- 1 | pipeline { 2 | agent any 3 | parameters { 4 | booleanParam(name: 'RC', defaultValue: false, description: 'Is this a Release Candidate?') 5 | } 6 | environment { 7 | VERSION = sh([ script: 'cd ./01/src && npm run env | grep "npm_package_version"', returnStdout: true ]).trim() 8 | VERSION_RC = "rc.2" 9 | } 10 | stages { 11 | stage('Audit tools') { 12 | steps { 13 | // sh ''' 14 | // git version 15 | // docker version 16 | // node --version 17 | // npm version 18 | // ''' 19 | auditTools() 20 | } 21 | } 22 | stage('Build') { 23 | environment { 24 | // VERSION_SUFFIX = "${sh(script:'if [ "${RC}" == "false" ] ; then echo -n "${VERSION_RC}+ci.${BUILD_NUMBER}"; else echo -n "${VERSION_RC}"; fi', returnStdout: true)}" 25 | VERSION_SUFFIX = getVersionSuffix() 26 | } 27 | steps { 28 | dir('./01/src') { 29 | echo "Building version ${VERSION} with suffix: ${VERSION_SUFFIX}" 30 | sh ''' 31 | npm install 32 | npm run build 33 | ''' 34 | } 35 | } 36 | } 37 | stage('Unit Test') { 38 | steps { 39 | dir('./01/src') { 40 | sh 'npm test' 41 | } 42 | } 43 | } 44 | stage('Publish') { 45 | when { 46 | expression { return params.RC } 47 | } 48 | steps { 49 | archiveArtifacts('01/src/app/') 50 | } 51 | } 52 | } 53 | } 54 | 55 | String getVersionSuffix() { 56 | if (params.RC) { 57 | return env.VERSION_RC 58 | } else { 59 | return env.VERSION_RC + '+ci' + env.BUILD_NUMBER 60 | } 61 | } 62 | 63 | void auditTools() { 64 | sh ''' 65 | git version 66 | docker version 67 | node --version 68 | npm version 69 | ''' 70 | } -------------------------------------------------------------------------------- /01/src/src/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { Server } from 'http'; 2 | import { app } from './app'; 3 | import supertest, { agent } from 'supertest'; 4 | import { TodoModel } from './models/todo.model'; 5 | 6 | import { todoDALFactory } from './dals/todos/todo.dal'; 7 | jest.mock('./dals/todos/todo.dal'); 8 | 9 | const todos = [ 10 | { id: 1, title: 'Learn node, please!!', completed: false }, 11 | { id: 2, title: 'Learn JS', completed: false }, 12 | { id: 3, title: 'Learn Docker', completed: false }, 13 | ]; 14 | 15 | (todoDALFactory as jest.Mock).mockImplementation(() => ({ 16 | getTodos() { 17 | return todos; 18 | }, 19 | getTodoById(id: number) { 20 | return todos.find((t) => t.id === id); 21 | }, 22 | createTodo(todo) { 23 | return { id: Date.now(), ...todo }; 24 | }, 25 | })); 26 | 27 | let server: Server; 28 | let request: supertest.SuperTest; 29 | 30 | describe('/api/', () => { 31 | describe('GET verb', () => { 32 | it('should return todos', async (done) => { 33 | // Arrange 34 | server = await app.listen(process.env.PORT); 35 | request = agent(server); 36 | 37 | // Act 38 | const response = await request.get('/api/'); 39 | const todos = response.body; 40 | 41 | // Assert 42 | expect(todos.length).toBe(3); 43 | removeServer(done); 44 | }); 45 | }); 46 | 47 | describe('GET verb with id parameter', () => { 48 | it('should returna single todo', async (done) => { 49 | // Arrange 50 | server = await app.listen(process.env.PORT); 51 | request = agent(server); 52 | 53 | // Act 54 | const response = await request.get('/api/1/'); 55 | const todo = response.body; 56 | 57 | // Assert 58 | expect(todo.id).toBe(1); 59 | expect(todo.title).toBe('Learn node, please!!'); 60 | removeServer(done); 61 | }); 62 | }); 63 | 64 | describe('POST verb creates a new todo', () => { 65 | it('should create a new todo', async (done) => { 66 | // Arrange 67 | const todo: TodoModel = { 68 | id: 0, 69 | title: 'New Todo', 70 | completed: false, 71 | dueDate: '2020-11-27', 72 | }; 73 | 74 | // Act 75 | const response = await request.post('/api/').send(todo); 76 | 77 | // Assert 78 | expect(response.status).toBe(201); 79 | removeServer(done); 80 | }); 81 | }); 82 | }); 83 | 84 | const removeServer = (done) => { 85 | server.close(); 86 | 87 | server.on('close', () => { 88 | done(); 89 | }); 90 | }; 91 | -------------------------------------------------------------------------------- /02/src/src/app.spec.ts: -------------------------------------------------------------------------------- 1 | import { Server } from 'http'; 2 | import { app } from './app'; 3 | import supertest, { agent } from 'supertest'; 4 | import { TodoModel } from './models/todo.model'; 5 | 6 | import { todoDALFactory } from './dals/todos/todo.dal'; 7 | jest.mock('./dals/todos/todo.dal'); 8 | 9 | const todos = [ 10 | { id: 1, title: 'Learn node, please!!', completed: false }, 11 | { id: 2, title: 'Learn JS', completed: false }, 12 | { id: 3, title: 'Learn Docker', completed: false }, 13 | ]; 14 | 15 | (todoDALFactory as jest.Mock).mockImplementation(() => ({ 16 | getTodos() { 17 | return todos; 18 | }, 19 | getTodoById(id: number) { 20 | return todos.find((t) => t.id === id); 21 | }, 22 | createTodo(todo) { 23 | return { id: Date.now(), ...todo }; 24 | }, 25 | })); 26 | 27 | let server: Server; 28 | let request: supertest.SuperTest; 29 | 30 | describe('/api/', () => { 31 | describe('GET verb', () => { 32 | it('should return todos', async (done) => { 33 | // Arrange 34 | server = await app.listen(process.env.PORT); 35 | request = agent(server); 36 | 37 | // Act 38 | const response = await request.get('/api/'); 39 | const todos = response.body; 40 | 41 | // Assert 42 | expect(todos.length).toBe(3); 43 | removeServer(done); 44 | }); 45 | }); 46 | 47 | describe('GET verb with id parameter', () => { 48 | it('should returna single todo', async (done) => { 49 | // Arrange 50 | server = await app.listen(process.env.PORT); 51 | request = agent(server); 52 | 53 | // Act 54 | const response = await request.get('/api/1/'); 55 | const todo = response.body; 56 | 57 | // Assert 58 | expect(todo.id).toBe(1); 59 | expect(todo.title).toBe('Learn node, please!!'); 60 | removeServer(done); 61 | }); 62 | }); 63 | 64 | describe('POST verb creates a new todo', () => { 65 | it('should create a new todo', async (done) => { 66 | // Arrange 67 | const todo: TodoModel = { 68 | id: 0, 69 | title: 'New Todo', 70 | completed: false, 71 | dueDate: '2020-11-27', 72 | }; 73 | 74 | // Act 75 | const response = await request.post('/api/').send(todo); 76 | 77 | // Assert 78 | expect(response.status).toBe(201); 79 | removeServer(done); 80 | }); 81 | }); 82 | }); 83 | 84 | const removeServer = (done) => { 85 | server.close(); 86 | 87 | server.on('close', () => { 88 | done(); 89 | }); 90 | }; 91 | -------------------------------------------------------------------------------- /01/src/src/app.ts: -------------------------------------------------------------------------------- 1 | import bodyParser from 'body-parser'; 2 | import 'dotenv/config'; 3 | import express from 'express'; 4 | import { startConnection } from './dals/dataAccess'; 5 | import { todoDALFactory } from './dals/todos/todo.dal'; 6 | import { mapTodoEntity, mapTodoEntityCollection, mapTodoModel } from './models/todo.model'; 7 | 8 | export const app = express(); 9 | 10 | app.use(bodyParser.urlencoded({ extended: true })); 11 | app.use(bodyParser.json()); 12 | 13 | // ------ The API implementation 14 | export const db = startConnection({ 15 | host: process.env.DB_HOST, 16 | user: process.env.DB_USER, 17 | password: process.env.DB_PASSWORD, 18 | port: +process.env.DB_PORT!, 19 | database: process.env.DB_NAME, 20 | dbVersion: process.env.DB_VERSION!, 21 | }); 22 | 23 | let todoDAL; 24 | const retrieveTodoDAL = () => { 25 | if(!todoDAL) { 26 | return todoDALFactory(db); 27 | } 28 | return todoDAL; 29 | }; 30 | 31 | app.get('/api/', async (_, res) => { 32 | todoDAL = retrieveTodoDAL(); 33 | const todoEntities = await todoDAL.getTodos(); 34 | res.send(mapTodoEntityCollection(todoEntities)); 35 | }); 36 | 37 | app.get('/api/:id/', async (req, res) => { 38 | const { id } = req.params; 39 | todoDAL = retrieveTodoDAL(); 40 | const todoEntity = await todoDAL.getTodoById(+id); 41 | res.send(mapTodoEntity(todoEntity)); 42 | }); 43 | 44 | app.post('/api/', async (req, res) => { 45 | try { 46 | todoDAL = retrieveTodoDAL(); 47 | await todoDAL.createTodo(mapTodoModel(req.body)); 48 | res.status(201); 49 | res.send('ok'); 50 | } catch (error) { 51 | console.error(error); 52 | res.status(500); 53 | res.send('error'); 54 | } 55 | }); 56 | 57 | app.patch('/api/:id/', async (req, res) => { 58 | try { 59 | const { id } = req.params; 60 | todoDAL = retrieveTodoDAL(); 61 | await todoDAL.updateTodo(+id, mapTodoModel(req.body)); 62 | res.status(200); 63 | res.send('ok') 64 | } catch (error) { 65 | console.error(error); 66 | res.status(500); 67 | res.send('error'); 68 | } 69 | }); 70 | 71 | app.put('/api/', async (_, res) => { 72 | try { 73 | todoDAL = retrieveTodoDAL(); 74 | await todoDAL.resolveTodos(); 75 | res.status(200); 76 | res.send('ok'); 77 | } catch (error) { 78 | console.error(error); 79 | res.status(500); 80 | res.send('error'); 81 | } 82 | }); 83 | 84 | app.delete('/api/:id/', async (req, res) => { 85 | try { 86 | const { id } = req.params; 87 | todoDAL = retrieveTodoDAL(); 88 | await todoDAL.deleteTodoById(+id); 89 | res.status(204); 90 | res.send('ok'); 91 | } catch (error) { 92 | console.error(error); 93 | res.status(500); 94 | res.send('error'); 95 | } 96 | }); 97 | 98 | const PORT = process.env.PORT || 3000; 99 | 100 | if (process.env.NODE_ENV !== 'test') { 101 | console.log('execute'); 102 | app.listen(PORT, () => { 103 | console.log(`Server running on port ${PORT}`); 104 | }); 105 | } 106 | -------------------------------------------------------------------------------- /02/src/src/app.ts: -------------------------------------------------------------------------------- 1 | import bodyParser from 'body-parser'; 2 | import 'dotenv/config'; 3 | import express from 'express'; 4 | import { startConnection } from './dals/dataAccess'; 5 | import { todoDALFactory } from './dals/todos/todo.dal'; 6 | import { mapTodoEntity, mapTodoEntityCollection, mapTodoModel } from './models/todo.model'; 7 | 8 | export const app = express(); 9 | 10 | app.use(bodyParser.urlencoded({ extended: true })); 11 | app.use(bodyParser.json()); 12 | 13 | // ------ The API implementation 14 | export const db = startConnection({ 15 | host: process.env.DB_HOST, 16 | user: process.env.DB_USER, 17 | password: process.env.DB_PASSWORD, 18 | port: +process.env.DB_PORT!, 19 | database: process.env.DB_NAME, 20 | dbVersion: process.env.DB_VERSION!, 21 | }); 22 | 23 | let todoDAL; 24 | const retrieveTodoDAL = () => { 25 | if(!todoDAL) { 26 | return todoDALFactory(db); 27 | } 28 | return todoDAL; 29 | }; 30 | 31 | app.get('/api/', async (_, res) => { 32 | todoDAL = retrieveTodoDAL(); 33 | const todoEntities = await todoDAL.getTodos(); 34 | res.send(mapTodoEntityCollection(todoEntities)); 35 | }); 36 | 37 | app.get('/api/:id/', async (req, res) => { 38 | const { id } = req.params; 39 | todoDAL = retrieveTodoDAL(); 40 | const todoEntity = await todoDAL.getTodoById(+id); 41 | res.send(mapTodoEntity(todoEntity)); 42 | }); 43 | 44 | app.post('/api/', async (req, res) => { 45 | try { 46 | todoDAL = retrieveTodoDAL(); 47 | await todoDAL.createTodo(mapTodoModel(req.body)); 48 | res.status(201); 49 | res.send('ok'); 50 | } catch (error) { 51 | console.error(error); 52 | res.status(500); 53 | res.send('error'); 54 | } 55 | }); 56 | 57 | app.patch('/api/:id/', async (req, res) => { 58 | try { 59 | const { id } = req.params; 60 | todoDAL = retrieveTodoDAL(); 61 | await todoDAL.updateTodo(+id, mapTodoModel(req.body)); 62 | res.status(200); 63 | res.send('ok') 64 | } catch (error) { 65 | console.error(error); 66 | res.status(500); 67 | res.send('error'); 68 | } 69 | }); 70 | 71 | app.put('/api/', async (_, res) => { 72 | try { 73 | todoDAL = retrieveTodoDAL(); 74 | await todoDAL.resolveTodos(); 75 | res.status(200); 76 | res.send('ok'); 77 | } catch (error) { 78 | console.error(error); 79 | res.status(500); 80 | res.send('error'); 81 | } 82 | }); 83 | 84 | app.delete('/api/:id/', async (req, res) => { 85 | try { 86 | const { id } = req.params; 87 | todoDAL = retrieveTodoDAL(); 88 | await todoDAL.deleteTodoById(+id); 89 | res.status(204); 90 | res.send('ok'); 91 | } catch (error) { 92 | console.error(error); 93 | res.status(500); 94 | res.send('error'); 95 | } 96 | }); 97 | 98 | const PORT = process.env.PORT || 3000; 99 | 100 | if (process.env.NODE_ENV !== 'test') { 101 | console.log('execute'); 102 | app.listen(PORT, () => { 103 | console.log(`Server running on port ${PORT}`); 104 | }); 105 | } 106 | -------------------------------------------------------------------------------- /01/src/src/app.test.ts: -------------------------------------------------------------------------------- 1 | import { Server } from 'http'; 2 | import { app, db as Knex } from './app'; 3 | import supertest, { agent } from 'supertest'; 4 | import { TodoEntity } from './dals/todos/todo.entity'; 5 | import { TodoModel } from './models/todo.model'; 6 | 7 | let server: Server; 8 | let request: supertest.SuperTest; 9 | 10 | beforeEach(async () => { 11 | server = await app.listen(process.env.PORT); 12 | request = agent(server); 13 | await Knex.from('todos').delete(); 14 | }); 15 | 16 | afterEach(() => { 17 | server.close(); 18 | }); 19 | 20 | afterAll(async () => { 21 | await Knex.destroy(); 22 | }); 23 | 24 | describe('/api/', () => { 25 | describe('GET verb', () => { 26 | it('should return todos', async () => { 27 | // Arrange 28 | await Promise.all([ 29 | insertTodo({ id: 1, title: 'Learn node', completed: false }), 30 | insertTodo({ id: 2, title: 'Learn JS', completed: false }), 31 | insertTodo({ id: 3, title: 'Learn Docker', completed: false }), 32 | ]); 33 | 34 | // Act 35 | const response = await request.get('/api/'); 36 | const todos = response.body; 37 | 38 | // Assert 39 | expect(todos.length).toBe(3); 40 | }); 41 | }); 42 | 43 | describe('GET verb with id parameter', () => { 44 | it('should return a single todo', async () => { 45 | // Arrange 46 | await insertTodo({ id: 1, title: 'Learn node', completed: false }); 47 | 48 | // Act 49 | const response = await request.get('/api/1/'); 50 | const todo = response.body; 51 | 52 | // Assert 53 | expect(todo.id).toBe(1); 54 | expect(todo.title).toBe('Learn node'); 55 | }); 56 | }); 57 | 58 | describe('POST verb creates a new todo', () => { 59 | it('should create a new todo', async () => { 60 | // Arrange 61 | const todo: TodoModel = { 62 | id: 0, 63 | title: 'New Todo', 64 | completed: false, 65 | dueDate: '2020-11-27', 66 | }; 67 | 68 | // Act 69 | const response = await request.post('/api/').send(todo); 70 | 71 | // Assert 72 | expect(response.status).toBe(201); 73 | }); 74 | }); 75 | 76 | describe('PATCH verb updates related fields', () => { 77 | it('should return ok status code', async () => { 78 | // Arrange 79 | await insertTodo({ id: 1, title: 'Learn node', completed: false }); 80 | 81 | // Act 82 | const response = await request.patch('/api/1').send({ completed: true }); 83 | 84 | // Assert 85 | expect(response.status).toBe(200); 86 | }); 87 | }); 88 | 89 | describe('PUT verb completes all todos', () => { 90 | it('should return ok status', async () => { 91 | // Arrange 92 | await Promise.all([ 93 | insertTodo({ id: 1, title: 'Learn node', completed: false }), 94 | insertTodo({ id: 2, title: 'Learn JS', completed: false }), 95 | insertTodo({ id: 3, title: 'Learn Docker', completed: false }), 96 | ]); 97 | 98 | // Act 99 | const response = await request.put('/api/'); 100 | 101 | // Assert 102 | expect(response.status).toBe(200); 103 | }); 104 | }); 105 | 106 | describe('DELETE verb remove a given todo', () => { 107 | it('should return ok status code', async () => { 108 | // Arrange 109 | await insertTodo({ id: 1, title: 'Learn node', completed: false }); 110 | 111 | // Act 112 | const response = await request.delete('/api/1/'); 113 | 114 | // Assert 115 | expect(response.status).toBe(204); 116 | }); 117 | }); 118 | }); 119 | 120 | const insertTodo = ({ id, title, completed }: { id: number; title: string; completed: boolean }): Promise => 121 | Knex('todos') 122 | .insert({ id, title, completed }, '*') 123 | .then(([todo]) => todo); 124 | -------------------------------------------------------------------------------- /02/src/src/app.test.ts: -------------------------------------------------------------------------------- 1 | import { Server } from 'http'; 2 | import { app, db as Knex } from './app'; 3 | import supertest, { agent } from 'supertest'; 4 | import { TodoEntity } from './dals/todos/todo.entity'; 5 | import { TodoModel } from './models/todo.model'; 6 | 7 | let server: Server; 8 | let request: supertest.SuperTest; 9 | 10 | beforeEach(async () => { 11 | server = await app.listen(process.env.PORT); 12 | request = agent(server); 13 | await Knex.from('todos').delete(); 14 | }); 15 | 16 | afterEach(() => { 17 | server.close(); 18 | }); 19 | 20 | afterAll(async () => { 21 | await Knex.destroy(); 22 | }); 23 | 24 | describe('/api/', () => { 25 | describe('GET verb', () => { 26 | it('should return todos', async () => { 27 | // Arrange 28 | await Promise.all([ 29 | insertTodo({ id: 1, title: 'Learn node', completed: false }), 30 | insertTodo({ id: 2, title: 'Learn JS', completed: false }), 31 | insertTodo({ id: 3, title: 'Learn Docker', completed: false }), 32 | ]); 33 | 34 | // Act 35 | const response = await request.get('/api/'); 36 | const todos = response.body; 37 | 38 | // Assert 39 | expect(todos.length).toBe(3); 40 | }); 41 | }); 42 | 43 | describe('GET verb with id parameter', () => { 44 | it('should return a single todo', async () => { 45 | // Arrange 46 | await insertTodo({ id: 1, title: 'Learn node', completed: false }); 47 | 48 | // Act 49 | const response = await request.get('/api/1/'); 50 | const todo = response.body; 51 | 52 | // Assert 53 | expect(todo.id).toBe(1); 54 | expect(todo.title).toBe('Learn node'); 55 | }); 56 | }); 57 | 58 | describe('POST verb creates a new todo', () => { 59 | it('should create a new todo', async () => { 60 | // Arrange 61 | const todo: TodoModel = { 62 | id: 0, 63 | title: 'New Todo', 64 | completed: false, 65 | dueDate: '2020-11-27', 66 | }; 67 | 68 | // Act 69 | const response = await request.post('/api/').send(todo); 70 | 71 | // Assert 72 | expect(response.status).toBe(201); 73 | }); 74 | }); 75 | 76 | describe('PATCH verb updates related fields', () => { 77 | it('should return ok status code', async () => { 78 | // Arrange 79 | await insertTodo({ id: 1, title: 'Learn node', completed: false }); 80 | 81 | // Act 82 | const response = await request.patch('/api/1').send({ completed: true }); 83 | 84 | // Assert 85 | expect(response.status).toBe(200); 86 | }); 87 | }); 88 | 89 | describe('PUT verb completes all todos', () => { 90 | it('should return ok status', async () => { 91 | // Arrange 92 | await Promise.all([ 93 | insertTodo({ id: 1, title: 'Learn node', completed: false }), 94 | insertTodo({ id: 2, title: 'Learn JS', completed: false }), 95 | insertTodo({ id: 3, title: 'Learn Docker', completed: false }), 96 | ]); 97 | 98 | // Act 99 | const response = await request.put('/api/'); 100 | 101 | // Assert 102 | expect(response.status).toBe(200); 103 | }); 104 | }); 105 | 106 | describe('DELETE verb remove a given todo', () => { 107 | it('should return ok status code', async () => { 108 | // Arrange 109 | await insertTodo({ id: 1, title: 'Learn node', completed: false }); 110 | 111 | // Act 112 | const response = await request.delete('/api/1/'); 113 | 114 | // Assert 115 | expect(response.status).toBe(204); 116 | }); 117 | }); 118 | }); 119 | 120 | const insertTodo = ({ id, title, completed }: { id: number; title: string; completed: boolean }): Promise => 121 | Knex('todos') 122 | .insert({ id, title, completed }, '*') 123 | .then(([todo]) => todo); 124 | -------------------------------------------------------------------------------- /01/src/README.md: -------------------------------------------------------------------------------- 1 | # Todo App API 2 | 3 | ## Variables de Entorno 4 | 5 | ```ini 6 | NODE_ENV= 7 | PORT= 8 | DB_HOST= 9 | DB_USER= 10 | DB_PASSWORD= 11 | DB_PORT= 12 | DB_NAME= 13 | DB_VERSION= 14 | ``` 15 | 16 | * **NODE_ENV** El entorno en el que nosencontramos 17 | * **PORT** El puerto donde va a correr la aplicación 18 | * **DB_HOST** El `host` donde se rncuentra corriendo la base de datos 19 | * **DB_USER** El usuario con el cual accedemos a esa base de datos 20 | * **DB_PASSWORD** El password asociado a ese usuario 21 | * **DB_PORT** El puerto en el cual escucha la base de datos 22 | * **DB_NAME** El nombre asociado a la base de datos 23 | * **DB_VERSION** La versión del motor de la base de datos 24 | 25 | ## Arrancar la Base de Datos usando Docker 26 | 27 | Construimos primero la imagen del servidor de la base de datos 28 | 29 | ```bash 30 | $ docker build -t lemoncode/postgres_todo_server -f Dockerfile.todos_db . 31 | ``` 32 | 33 | Levantamos la base de datos construida anteriormente. 34 | 35 | ```bash 36 | $ docker run -d -p 5432:5432 -v todos:/var/lib/postgresql/data --name postgres_todo_server lemoncode/postgres_todo_server 37 | ``` 38 | 39 | > NOTA: Para que la base de datos este, el volumen al que estamos mapeando debe de estar vacío, si no la inicialización no ocurrirá. 40 | 41 | Para comprobar que la base de datos se encuentra en ejecución debemos ejecutar 42 | 43 | ```bash 44 | $ docker exec -it postgres_todo_server psql -U postgres 45 | ``` 46 | 47 | Y dentro del listado de la bases de datos, deberíamos ver `todos_db`, para listar las bases de datos `\l`, y después `enter`. 48 | 49 | ``` 50 | postgres=# \l 51 | List of databases 52 | Name | Owner | Encoding | Collate | Ctype | Access privileges 53 | -----------+----------+----------+------------+------------+----------------------- 54 | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | 55 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + 56 | | | | | | postgres=CTc/postgres 57 | template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + 58 | | | | | | postgres=CTc/postgres 59 | todos_db | postgres | UTF8 | en_US.utf8 | en_US.utf8 | 60 | (4 rows) 61 | ``` 62 | 63 | Para conectar con la base de datos en nuestro entorno local, debemos tener creado un fichero `.env` en el raíz, que se vea de la siguiente manera: 64 | 65 | ```ini 66 | DB_HOST=localhost 67 | DB_USER=postgres 68 | DB_PASSWORD=postgres 69 | DB_PORT=5432 70 | DB_NAME=todos_db 71 | DB_VERSION=10.4 72 | ``` 73 | 74 | Para generar las distintas tablas dentro de nuestra base de datos ejecutaremos: 75 | 76 | ```bash 77 | $ $(npm bin)/knex migrate:latest 78 | ``` 79 | 80 | Para tener una semilla de datos, podemos lanzar 81 | 82 | ```bash 83 | $ $(npm bin)/knex seed:run 84 | ``` 85 | 86 | ## Ejecutando Unit Tests 87 | 88 | Para ejecutar los unit tests, simplemente, tenemos que lanzar el siguiente comando 89 | 90 | ```bash 91 | $ npm run test 92 | ``` 93 | 94 | ## Ejecutando Tests de Integración 95 | 96 | Para jecutar los test de integración necesitamos que la base de datos, corriendo y lista para recibir peticiones: 97 | 98 | ```bash 99 | $ docker run -d -p 5432:5432 -v todos:/var/lib/postgresql/data --name postgres_todo_server lemoncode/postgres_todo_server 100 | ``` 101 | 102 | Después de hacer esto simplemente tenemos que ejecutar: 103 | 104 | ```bash 105 | $ npm run test:e2e 106 | ``` 107 | 108 | ## Ejecutando la aplicación con Docker en nuestro entorno local 109 | 110 | ```bash 111 | $ docker build -t lemoncode/todo-app . 112 | ``` 113 | 114 | ```bash 115 | $ docker network create lemoncode 116 | ``` 117 | 118 | ```bash 119 | $ docker run -d -v todos:/var/lib/postgresql/data \ 120 | --network lemoncode \ 121 | --name pg-todo-server \ 122 | lemoncode/postgres_todo_server 123 | ``` 124 | 125 | ```bash 126 | $ docker run -d --rm -p 3000:3000 \ 127 | --network lemoncode \ 128 | -e NODE_ENV=production \ 129 | -e PORT=3000 \ 130 | -e DB_HOST=pg-todo-server \ 131 | -e DB_USER=postgres \ 132 | -e DB_PASSWORD=postgres \ 133 | -e DB_PORT=5432 \ 134 | -e DB_NAME=todos_db \ 135 | -e DB_VERSION=10.4 \ 136 | lemoncode/todo-app 137 | ``` 138 | 139 | ```bash 140 | $ curl localhost:3000/api/ 141 | [{"id":1,"title":"Learn GitHub Actions","completed":false,"order":null,"dueDate":null},{"id":2,"title":"Learn Groovy","completed":false,"order":null,"dueDate":null},{"id":3,"title":"Learn EKS","completed":false,"order":null,"dueDate":null}] 142 | ``` 143 | 144 | 145 | 146 | -------------------------------------------------------------------------------- /02/src/README.md: -------------------------------------------------------------------------------- 1 | # Todo App API 2 | 3 | ## Variables de Entorno 4 | 5 | ```ini 6 | NODE_ENV= 7 | PORT= 8 | DB_HOST= 9 | DB_USER= 10 | DB_PASSWORD= 11 | DB_PORT= 12 | DB_NAME= 13 | DB_VERSION= 14 | ``` 15 | 16 | * **NODE_ENV** El entorno en el que nosencontramos 17 | * **PORT** El puerto donde va a correr la aplicación 18 | * **DB_HOST** El `host` donde se rncuentra corriendo la base de datos 19 | * **DB_USER** El usuario con el cual accedemos a esa base de datos 20 | * **DB_PASSWORD** El password asociado a ese usuario 21 | * **DB_PORT** El puerto en el cual escucha la base de datos 22 | * **DB_NAME** El nombre asociado a la base de datos 23 | * **DB_VERSION** La versión del motor de la base de datos 24 | 25 | ## Arrancar la Base de Datos usando Docker 26 | 27 | Construimos primero la imagen del servidor de la base de datos 28 | 29 | ```bash 30 | $ docker build -t lemoncode/postgres_todo_server -f Dockerfile.todos_db . 31 | ``` 32 | 33 | Levantamos la base de datos construida anteriormente. 34 | 35 | ```bash 36 | $ docker run -d -p 5432:5432 -v todos:/var/lib/postgresql/data --name postgres_todo_server lemoncode/postgres_todo_server 37 | ``` 38 | 39 | > NOTA: Para que la base de datos este, el volumen al que estamos mapeando debe de estar vacío, si no la inicialización no ocurrirá. 40 | 41 | Para comprobar que la base de datos se encuentra en ejecución debemos ejecutar 42 | 43 | ```bash 44 | $ docker exec -it postgres_todo_server psql -U postgres 45 | ``` 46 | 47 | Y dentro del listado de la bases de datos, deberíamos ver `todos_db`, para listar las bases de datos `\l`, y después `enter`. 48 | 49 | ``` 50 | postgres=# \l 51 | List of databases 52 | Name | Owner | Encoding | Collate | Ctype | Access privileges 53 | -----------+----------+----------+------------+------------+----------------------- 54 | postgres | postgres | UTF8 | en_US.utf8 | en_US.utf8 | 55 | template0 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + 56 | | | | | | postgres=CTc/postgres 57 | template1 | postgres | UTF8 | en_US.utf8 | en_US.utf8 | =c/postgres + 58 | | | | | | postgres=CTc/postgres 59 | todos_db | postgres | UTF8 | en_US.utf8 | en_US.utf8 | 60 | (4 rows) 61 | ``` 62 | 63 | Para conectar con la base de datos en nuestro entorno local, debemos tener creado un fichero `.env` en el raíz, que se vea de la siguiente manera: 64 | 65 | ```ini 66 | DB_HOST=localhost 67 | DB_USER=postgres 68 | DB_PASSWORD=postgres 69 | DB_PORT=5432 70 | DB_NAME=todos_db 71 | DB_VERSION=10.4 72 | ``` 73 | 74 | Para generar las distintas tablas dentro de nuestra base de datos ejecutaremos: 75 | 76 | ```bash 77 | $ $(npm bin)/knex migrate:latest 78 | ``` 79 | 80 | Para tener una semilla de datos, podemos lanzar 81 | 82 | ```bash 83 | $ $(npm bin)/knex seed:run 84 | ``` 85 | 86 | ## Ejecutando Unit Tests 87 | 88 | Para ejecutar los unit tests, simplemente, tenemos que lanzar el siguiente comando 89 | 90 | ```bash 91 | $ npm run test 92 | ``` 93 | 94 | ## Ejecutando Tests de Integración 95 | 96 | Para jecutar los test de integración necesitamos que la base de datos, corriendo y lista para recibir peticiones: 97 | 98 | ```bash 99 | $ docker run -d -p 5432:5432 -v todos:/var/lib/postgresql/data --name postgres_todo_server lemoncode/postgres_todo_server 100 | ``` 101 | 102 | Después de hacer esto simplemente tenemos que ejecutar: 103 | 104 | ```bash 105 | $ npm run test:e2e 106 | ``` 107 | 108 | ## Ejecutando la aplicación con Docker en nuestro entorno local 109 | 110 | ```bash 111 | $ docker build -t lemoncode/todo-app . 112 | ``` 113 | 114 | ```bash 115 | $ docker network create lemoncode 116 | ``` 117 | 118 | ```bash 119 | $ docker run -d -v todos:/var/lib/postgresql/data \ 120 | --network lemoncode \ 121 | --name pg-todo-server \ 122 | lemoncode/postgres_todo_server 123 | ``` 124 | 125 | ```bash 126 | $ docker run -d --rm -p 3000:3000 \ 127 | --network lemoncode \ 128 | -e NODE_ENV=production \ 129 | -e PORT=3000 \ 130 | -e DB_HOST=pg-todo-server \ 131 | -e DB_USER=postgres \ 132 | -e DB_PASSWORD=postgres \ 133 | -e DB_PORT=5432 \ 134 | -e DB_NAME=todos_db \ 135 | -e DB_VERSION=10.4 \ 136 | lemoncode/todo-app 137 | ``` 138 | 139 | ```bash 140 | $ curl localhost:3000/api/ 141 | [{"id":1,"title":"Learn GitHub Actions","completed":false,"order":null,"dueDate":null},{"id":2,"title":"Learn Groovy","completed":false,"order":null,"dueDate":null},{"id":3,"title":"Learn EKS","completed":false,"order":null,"dueDate":null}] 142 | ``` 143 | 144 | 145 | 146 | --------------------------------------------------------------------------------