├── .gitignore ├── 01 Non blocking ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ ├── apiIceAndFire.js │ ├── main.js │ ├── mapper.js │ └── printService.js ├── 02 How to avoid blocking ├── README.md ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ ├── apiIceAndFire.js │ ├── main.js │ ├── mapper.js │ └── printService.js ├── 03 Run to completion ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ └── main.js ├── 04 Operation Order ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ └── main.js ├── 05 User interactions ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ └── main.js ├── 06 Source Timers ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ └── main.js ├── 07 Timer Gotchas ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ └── main.js ├── 08 Not always async ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ ├── main.js │ └── utils.js ├── 09 Web Workers ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ ├── background.js │ ├── inline.js │ └── longTask.js ├── 10 Event listeners are sync ├── README.md ├── package-lock.json ├── package.json └── src │ ├── content │ └── site.css │ ├── index.html │ └── js │ └── main.js ├── 11 Axios ├── 00_seed │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ └── bookAPI.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ └── view │ │ └── uiBuilder.js ├── 01_get │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ └── bookAPI.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ └── view │ │ └── uiBuilder.js ├── 02_post │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ └── bookAPI.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ └── view │ │ └── uiBuilder.js ├── 03_callbacks │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ └── bookAPI.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ └── view │ │ └── uiBuilder.js ├── 04_promises │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ └── bookAPI.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ └── view │ │ └── uiBuilder.js ├── 05_all │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ ├── authorAPI.js │ │ └── bookAPI.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ └── view │ │ └── uiBuilder.js ├── 06_multiple_params │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ ├── authorAPI.js │ │ └── bookAPI.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ ├── pages │ │ ├── booksearch.html │ │ └── booksearch │ │ │ └── main.js │ │ └── view │ │ └── uiBuilder.js ├── 07_interceptors │ ├── README.md │ ├── README_99.MD │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ ├── accessAPI.js │ │ ├── authorAPI.js │ │ ├── bookAPI.js │ │ └── interceptors.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ ├── pages │ │ ├── access.html │ │ ├── access │ │ │ └── main.js │ │ ├── booksearch.html │ │ └── booksearch │ │ │ └── main.js │ │ └── view │ │ └── uiBuilder.js ├── 07b_interceptors │ ├── README.MD │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ ├── interceptors.js │ │ └── loginAPI.js │ │ ├── app.js │ │ └── asyncApi.js ├── 08_cancel_token │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ ├── accessAPI.js │ │ ├── authorAPI.js │ │ ├── bookAPI.js │ │ └── interceptors.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ ├── pages │ │ ├── access.html │ │ ├── access │ │ │ └── main.js │ │ ├── booksearch.html │ │ └── booksearch │ │ │ └── main.js │ │ └── view │ │ └── uiBuilder.js ├── 08b_cancel_token │ ├── README.MD │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ ├── interceptors.js │ │ └── loginAPI.js │ │ ├── app.js │ │ └── asyncApi.js ├── 09_patch │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── API │ │ ├── accessAPI.js │ │ ├── authorAPI.js │ │ ├── bookAPI.js │ │ └── interceptors.js │ │ ├── app.js │ │ ├── content │ │ └── site.css │ │ ├── pages │ │ ├── access.html │ │ ├── access │ │ │ └── main.js │ │ ├── bookpatch.html │ │ ├── bookpatch │ │ │ └── main.js │ │ ├── booksearch.html │ │ └── booksearch │ │ │ └── main.js │ │ └── view │ │ └── uiBuilder.js └── 10_delete │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ ├── API │ ├── accessAPI.js │ ├── authorAPI.js │ ├── bookAPI.js │ └── interceptors.js │ ├── app.js │ ├── content │ └── site.css │ ├── pages │ ├── access.html │ ├── access │ │ └── main.js │ ├── bookpatch.html │ ├── bookpatch │ │ └── main.js │ ├── booksearch.html │ └── booksearch │ │ └── main.js │ └── view │ └── uiBuilder.js ├── 12 Promises ├── 00_start │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package.json │ └── src │ │ ├── content │ │ └── site.css │ │ └── js │ │ ├── Drawer.js │ │ ├── Mapper.js │ │ ├── main.js │ │ └── weatherAPI.js ├── 01_promise_chaining │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package.json │ └── src │ │ ├── content │ │ └── site.css │ │ └── js │ │ ├── Drawer.js │ │ ├── Mapper.js │ │ ├── main.js │ │ └── weatherAPI.js ├── 02_promise_all │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package.json │ └── src │ │ ├── content │ │ └── site.css │ │ └── js │ │ ├── Drawer.js │ │ ├── Mapper.js │ │ ├── main.js │ │ └── weatherAPI.js ├── 03_avoiding_boilerplate_XMLHttpRequest │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package.json │ └── src │ │ ├── content │ │ └── site.css │ │ └── js │ │ ├── Drawer.js │ │ ├── Mapper.js │ │ ├── httpClient.js │ │ ├── main.js │ │ └── weatherAPI.js └── 04_promise_race │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package.json │ └── src │ ├── content │ └── site.css │ └── js │ ├── Drawer.js │ ├── Mapper.js │ ├── httpClient.js │ ├── main.js │ └── weatherAPI.js ├── 13 Fetch ├── 00_start │ ├── README.md │ ├── index.html │ ├── package.json │ └── src │ │ ├── content │ │ └── site.css │ │ └── js │ │ └── uiBuilder.js ├── 01_createRequestFactory │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── content │ │ └── site.css │ │ └── js │ │ ├── bookAPI.js │ │ ├── main.js │ │ └── uiBuilder.js └── 01_requestFactory │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ ├── content │ └── site.css │ └── js │ ├── bookAPI.js │ ├── main.js │ └── uiBuilder.js ├── 14 Async await ├── 00_start │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── api │ │ ├── BookApiClient.js │ │ └── bookAPI.js │ │ └── app.js ├── 01_errors │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── api │ │ ├── BookApiClient.js │ │ └── bookAPI.js │ │ └── app.js ├── 02_parallel │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ │ ├── api │ │ ├── BookApiClient.js │ │ └── bookAPI.js │ │ └── app.js └── 03_sequential │ ├── .babelrc │ ├── README.md │ ├── index.html │ ├── package-lock.json │ ├── package.json │ └── src │ ├── api │ ├── BookApiClient.js │ └── bookAPI.js │ └── app.js ├── LICENSE ├── README.md ├── auth ├── credentialResolver.js ├── index.js ├── package-lock.json └── package.json ├── docker-compose.yml ├── server-in-memory ├── README.md ├── package-lock.json ├── package.json ├── routes │ ├── cars.js │ ├── data │ │ └── cars.json │ └── users.js └── server.js ├── server-mock ├── config │ └── routes.json ├── mock-data │ └── data.json ├── package-lock.json └── package.json └── server-mongo ├── .dockerignore ├── .env ├── .vscode └── launch.json ├── Dockerfile ├── README.md ├── Routes ├── accessRoutes.js ├── authorRoutes.js └── bookRoutes.js ├── Tests ├── bookControllerTests.js └── bookIntegrationTests.js ├── app.js ├── controllers ├── accessController.js ├── accessControllerBusiness.js ├── authorController.js ├── authorControllerBusiness.js ├── bookByIdController.js ├── bookController.js └── bookControllerBusiness.js ├── gulpfile.js ├── middlewares └── bookMiddleware.js ├── models ├── authorModel.js └── bookModel.js ├── package-lock.json ├── package.json ├── parsers └── bookParser.js └── utils └── randomNumber.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (http://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # Typescript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | # .env 59 | 60 | # Build folders parcel 61 | dist/ 62 | .cache 63 | 64 | .DS_store 65 | -------------------------------------------------------------------------------- /01 Non blocking/README.md: -------------------------------------------------------------------------------- 1 | ## In order to get the code working: 2 | 3 | * Open terminal at the same level as `package.json`, and type: 4 | ```bash 5 | $ npm install 6 | ``` 7 | * After installation, we can run the application using: 8 | ```bash 9 | $ npm install 10 | ``` 11 | * To get results on the open browser app: 12 | * house: `House Lannister of Casterly Rock` 13 | * character input: `Jaime Lannister` 14 | 15 | ## In this demo we are going to study the non blocking nature of JavaScript 16 | 17 | * Just one thing can happen per time in JavaScript. But the required time to get data loaded is really short. Is not just to have a really good network capabilities, there is another reason. 18 | 19 | * Looking into the code this reason is not so obviously.We can think looking the `main.js` code that: 20 | * While this request `service.getHousesByName(houseInput.value, housesRequestConfig);` does not end... 21 | * This one `service.getCharactersByName(characterInput.value, charactersRequestConfig);` is not going to start 22 | 23 | * If we already have been working with callbacks, we are use to initialize a request and then proceed with the execution. 24 | 25 | * But if just one thing per time it's executed, how is this possible? 26 | 27 | * Opening the developer tools on Chrome, open the network tab. Here we can watch that the requests are sending concurrently. 28 | -------------------------------------------------------------------------------- /01 Non blocking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /01 Non blocking/src/content/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, Helvetica, sans-serif; 3 | margin: 0; 4 | } 5 | 6 | .content { 7 | margin: 3rem; 8 | } 9 | 10 | header { 11 | text-align: center; 12 | background-color: black; 13 | } 14 | 15 | header img { 16 | display: inline-block; 17 | min-width: 30%; 18 | max-height: 150px; 19 | } 20 | 21 | span { 22 | margin: auto 0; 23 | } 24 | 25 | button { 26 | border-radius: 3px / 5px; 27 | background-color: rgba(220, 220, 220, 0.9); 28 | border: 1.5px solid rgba(90,90,90,0.4); 29 | } 30 | 31 | button:hover { 32 | background-color: rgba(220, 220, 220, 0.2); 33 | } 34 | 35 | input { 36 | display: inline-block; 37 | border-radius: 3px / 5px; 38 | border: 1.5px solid rgba(90,90,90,0.4); 39 | background: rgba(220,220,220,0.16); 40 | padding: 0.2rem 0.4rem; 41 | margin-left: 0.5rem; 42 | width: 15rem; 43 | } 44 | 45 | .search { 46 | display: flex; 47 | flex-flow: row; 48 | } 49 | 50 | .search.search_item { 51 | flex-grow: 2; 52 | } 53 | 54 | .search.search_button { 55 | align-content: flex-end; 56 | } 57 | 58 | .display { 59 | display: flex; 60 | } 61 | 62 | .display .houses { 63 | flex-grow: 2; 64 | } 65 | 66 | .display .characters { 67 | flex-grow: 1; 68 | } 69 | -------------------------------------------------------------------------------- /01 Non blocking/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 |
14 | Game of Thrones 15 |
16 |
17 | 28 |
29 |
30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /01 Non blocking/src/js/apiIceAndFire.js: -------------------------------------------------------------------------------- 1 | const baseRequestFactory = ({err, callback}) => { 2 | const req = new XMLHttpRequest(); 3 | req.onload = callback; 4 | req.onerror = err; 5 | return req; 6 | }; 7 | 8 | let baseRequests = new WeakMap(); 9 | 10 | const getRequest = (config) => { 11 | let baseRequest = baseRequests.get(config); 12 | if (!baseRequest) { 13 | baseRequest = baseRequestFactory(config); 14 | baseRequests.set(config, baseRequest); 15 | } 16 | return baseRequest; 17 | }; 18 | 19 | const sendGetRequest = (req, url) => { 20 | req.open('get', url , true); 21 | req.send(); 22 | }; 23 | 24 | export default class Service { 25 | constructor() { 26 | this.baseUrl = 'https://www.anapioficeandfire.com/api'; 27 | } 28 | 29 | getHouseById(id, config) { 30 | const req = getRequest(config); 31 | const url = `${this.baseUrl}/houses/${id}`; 32 | sendGetRequest(req, url); 33 | } 34 | 35 | getCharacterById(id, config) { 36 | const req = getRequest(config); 37 | const url = `${this.baseUrl}/characters/${id}`; 38 | sendGetRequest(req, url); 39 | } 40 | 41 | getHousesByName(name, config) { 42 | const req = getRequest(config); 43 | const url = `${this.baseUrl}/houses?name=${name}`; 44 | sendGetRequest(req, url); 45 | } 46 | 47 | getCharactersByName(name, config) { 48 | const req = getRequest(config); 49 | const url = `${this.baseUrl}/characters?name=${name}`; 50 | sendGetRequest(req, url); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /01 Non blocking/src/js/mapper.js: -------------------------------------------------------------------------------- 1 | export const housesMap = (houses) => ( 2 | houses.map(h => ({ 3 | name: h.name, 4 | words: h.words, 5 | region: h.region, 6 | coatOfArms: h.coatOfArms 7 | })) 8 | ); 9 | 10 | export const caracthersMap = (characters) => ( 11 | characters.map(c => ({ 12 | name: c.name, 13 | born: c.born, 14 | aliases: [...c.aliases] 15 | })) 16 | ); -------------------------------------------------------------------------------- /01 Non blocking/src/js/printService.js: -------------------------------------------------------------------------------- 1 | const createElement = (field, fieldValue, tag) => { 2 | const element = document.createElement(tag); 3 | element.innerHTML = `${field}: ${fieldValue}`; 4 | return element; 5 | } 6 | 7 | const createList = (entries) => { 8 | const ulElement = document.createElement('ul'); 9 | entries.forEach((e) => { 10 | const liElement = document.createElement('li'); 11 | liElement.innerHTML = e; 12 | ulElement.appendChild(liElement); 13 | }); 14 | 15 | return ulElement; 16 | } 17 | 18 | const appendChildren = (node, ...children) => { 19 | children.forEach((c) => node.appendChild(c)); 20 | } 21 | 22 | export default class Printer { 23 | printHouses(houses, container) { 24 | houses.forEach(h => this.printHouse(h, container)); 25 | } 26 | 27 | printHouse(house, container) { 28 | const node = document.createElement('div'); 29 | const name = createElement('House name', house.name, 'h3'); 30 | const words = createElement('Words', house.words, 'h4'); 31 | appendChildren(node, name, words); 32 | document.getElementById(container).appendChild(node); 33 | } 34 | 35 | printCharacters(characters, container) { 36 | characters.forEach(c => this.printCharacter(c, container)); 37 | } 38 | 39 | printCharacter(character, container) { 40 | const node = document.createElement('div'); 41 | const name = createElement('Character name', character.name, 'h3'); 42 | const born = createElement('Born', character.born, 'h4'); 43 | const aliases = createList(character.aliases); 44 | appendChildren(node, name, born, aliases); 45 | document.getElementById(container).appendChild(node); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /02 How to avoid blocking/README.md: -------------------------------------------------------------------------------- 1 | ## In order to get the code working: 2 | 3 | * Open terminal at the same level as `package.json`, and type: 4 | ```bash 5 | $ npm install 6 | ``` 7 | * After installation, we can run the application using: 8 | ```bash 9 | $ npm install 10 | ``` 11 | * To get results on the open browser app: 12 | * house: `House Lannister of Casterly Rock` 13 | * character input: `Jaime Lannister` 14 | 15 | ## In this demo we are going to stablish the blocking nature of JavaScript 16 | 17 | ## Steps. 18 | 19 | ### 1. Open `src/js/apiIceAndFire.js` 20 | 21 | ```diff 22 | const sendGetRequest = (req, url) => { 23 | - req.open('get', url, true); 24 | + req.open('get', url, false); 25 | req.send(); 26 | }; 27 | 28 | ``` 29 | * This way `XHR` work SYNC. 30 | 31 | ### 2. Now if we change these values again we can watch that goes on parallel 32 | 33 | ```diff apiIceAndFire.js 34 | const sendGetRequest = (req, url) => { 35 | + req.open('get', url, true); 36 | - req.open('get', url, false); 37 | req.send(); 38 | }; 39 | 40 | ``` 41 | * Open and show results on developer tools. 42 | -------------------------------------------------------------------------------- /02 How to avoid blocking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /02 How to avoid blocking/src/content/site.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Arial, Helvetica, sans-serif; 3 | margin: 0; 4 | } 5 | 6 | .content { 7 | margin: 3rem; 8 | } 9 | 10 | header { 11 | text-align: center; 12 | background-color: black; 13 | } 14 | 15 | header img { 16 | display: inline-block; 17 | min-width: 30%; 18 | max-height: 150px; 19 | } 20 | 21 | span { 22 | margin: auto 0; 23 | } 24 | 25 | button { 26 | border-radius: 3px / 5px; 27 | background-color: rgba(220, 220, 220, 0.9); 28 | border: 1.5px solid rgba(90,90,90,0.4); 29 | } 30 | 31 | button:hover { 32 | background-color: rgba(220, 220, 220, 0.2); 33 | } 34 | 35 | input { 36 | display: inline-block; 37 | border-radius: 3px / 5px; 38 | border: 1.5px solid rgba(90,90,90,0.4); 39 | background: rgba(220,220,220,0.16); 40 | padding: 0.2rem 0.4rem; 41 | margin-left: 0.5rem; 42 | width: 15rem; 43 | } 44 | 45 | .search { 46 | display: flex; 47 | flex-flow: row; 48 | } 49 | 50 | .search.search_item { 51 | flex-grow: 2; 52 | } 53 | 54 | .search.search_button { 55 | align-content: flex-end; 56 | } 57 | 58 | .display { 59 | display: flex; 60 | } 61 | 62 | .display .houses { 63 | flex-grow: 2; 64 | } 65 | 66 | .display .characters { 67 | flex-grow: 1; 68 | } 69 | -------------------------------------------------------------------------------- /02 How to avoid blocking/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 |
14 | Game of Thrones 15 |
16 |
17 | 28 |
29 |
30 |
31 |
32 |
33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /02 How to avoid blocking/src/js/apiIceAndFire.js: -------------------------------------------------------------------------------- 1 | const baseRequestFactory = ({err, callback}) => { 2 | const req = new XMLHttpRequest(); 3 | req.onload = callback; 4 | req.onerror = err; 5 | return req; 6 | }; 7 | 8 | let baseRequests = new WeakMap(); 9 | 10 | const getRequest = (config) => { 11 | let baseRequest = baseRequests.get(config); 12 | if (!baseRequest) { 13 | baseRequest = baseRequestFactory(config); 14 | baseRequests.set(config, baseRequest); 15 | } 16 | return baseRequest; 17 | }; 18 | 19 | const sendGetRequest = (req, url) => { 20 | req.open('get', url, true); 21 | req.send(); 22 | }; 23 | 24 | export default class Service { 25 | constructor() { 26 | this.baseUrl = 'https://www.anapioficeandfire.com/api'; 27 | } 28 | 29 | getHouseById(id, config) { 30 | const req = getRequest(config); 31 | const url = `${this.baseUrl}/houses/${id}`; 32 | sendGetRequest(req, url); 33 | } 34 | 35 | getCharacterById(id, config) { 36 | const req = getRequest(config); 37 | const url = `${this.baseUrl}/characters/${id}`; 38 | sendGetRequest(req, url); 39 | } 40 | 41 | getHousesByName(name, config) { 42 | const req = getRequest(config); 43 | const url = `${this.baseUrl}/houses?name=${name}`; 44 | sendGetRequest(req, url); 45 | } 46 | 47 | getCharactersByName(name, config) { 48 | const req = getRequest(config); 49 | const url = `${this.baseUrl}/characters?name=${name}`; 50 | sendGetRequest(req, url); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /02 How to avoid blocking/src/js/mapper.js: -------------------------------------------------------------------------------- 1 | export const housesMap = (houses) => ( 2 | houses.map(h => ({ 3 | name: h.name, 4 | words: h.words, 5 | region: h.region, 6 | coatOfArms: h.coatOfArms 7 | })) 8 | ); 9 | 10 | export const caracthersMap = (characters) => ( 11 | characters.map(c => ({ 12 | name: c.name, 13 | born: c.born, 14 | aliases: [...c.aliases] 15 | })) 16 | ); -------------------------------------------------------------------------------- /02 How to avoid blocking/src/js/printService.js: -------------------------------------------------------------------------------- 1 | const createElement = (field, fieldValue, tag) => { 2 | const element = document.createElement(tag); 3 | element.innerHTML = `${field}: ${fieldValue}`; 4 | return element; 5 | } 6 | 7 | const createList = (entries) => { 8 | const ulElement = document.createElement('ul'); 9 | entries.forEach((e) => { 10 | const liElement = document.createElement('li'); 11 | liElement.innerHTML = e; 12 | ulElement.appendChild(liElement); 13 | }); 14 | 15 | return ulElement; 16 | } 17 | 18 | const appendChildren = (node, ...children) => { 19 | children.forEach((c) => node.appendChild(c)); 20 | } 21 | 22 | export default class Printer { 23 | printHouses(houses, container) { 24 | houses.forEach(h => this.printHouse(h, container)); 25 | } 26 | 27 | printHouse(house, container) { 28 | const node = document.createElement('div'); 29 | const name = createElement('House name', house.name, 'h3'); 30 | const words = createElement('Words', house.words, 'h4'); 31 | appendChildren(node, name, words); 32 | document.getElementById(container).appendChild(node); 33 | } 34 | 35 | printCharacters(characters, container) { 36 | characters.forEach(c => this.printCharacter(c, container)); 37 | } 38 | 39 | printCharacter(character, container) { 40 | const node = document.createElement('div'); 41 | const name = createElement('Character name', character.name, 'h3'); 42 | const born = createElement('Born', character.born, 'h4'); 43 | const aliases = createList(character.aliases); 44 | appendChildren(node, name, born, aliases); 45 | document.getElementById(container).appendChild(node); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /03 Run to completion/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /03 Run to completion/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /03 Run to completion/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /03 Run to completion/src/js/main.js: -------------------------------------------------------------------------------- 1 | document.onreadystatechange = () => { 2 | if(document.readyState === 'complete') { 3 | const btn = document.getElementById("button"); 4 | btn.addEventListener('click', (evt) => { 5 | evt.stopPropagation(); 6 | modifyPage(); 7 | blockingOperation(); 8 | }); 9 | 10 | const modifyPage = () => { 11 | // modify page 12 | document.body.style.backgroundColor = 'lime'; 13 | const p = document.createElement("p"); 14 | p.innerText = "let's add some text to the page"; 15 | document.body.appendChild(p); 16 | }; 17 | 18 | const blockingOperation = () => { 19 | // simulate blocking / long running operation 20 | const start = Date.now(); 21 | const delaySeconds = 10; 22 | while (Date.now() < start + delaySeconds * 1000) {} 23 | } 24 | } 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /04 Operation Order/README.md: -------------------------------------------------------------------------------- 1 | ## 1. Lets have a look to this code 2 | 3 | ```javascript 4 | document.onreadystatechange = () => { 5 | if(document.readyState === 'complete') { 6 | 7 | console.log('1'); 8 | 9 | setTimeout(() => { 10 | 11 | console.log('2'); 12 | 13 | setTimeout(() => { 14 | 15 | console.log('3'); 16 | 17 | }, 0); 18 | 19 | console.log('4'); 20 | 21 | }, 0); 22 | 23 | console.log('5'); 24 | 25 | } 26 | }; 27 | 28 | ``` 29 | 30 | 31 | ## 2. What is the order of the prinrt numbers in console? 32 | 33 | 1, 5, 2, 4, 3 34 | 35 | * `setTimeout`, is another function that plans another async function, as web request would do. 36 | * We obtain this result due to run to completion. 37 | * The browser gives to these function a min value of 4ms. 38 | -------------------------------------------------------------------------------- /04 Operation Order/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /04 Operation Order/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /04 Operation Order/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /04 Operation Order/src/js/main.js: -------------------------------------------------------------------------------- 1 | document.onreadystatechange = () => { 2 | if(document.readyState === 'complete') { 3 | 4 | console.log('1'); 5 | 6 | setTimeout(() => { 7 | 8 | console.log('2'); 9 | 10 | setTimeout(() => { 11 | 12 | console.log('3'); 13 | 14 | }, 0); 15 | 16 | console.log('4'); 17 | 18 | }, 0); 19 | 20 | console.log('5'); 21 | 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /05 User interactions/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /05 User interactions/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | 31 | .drag { 32 | background-color: cyan; 33 | width: 200px; 34 | height: 200px; 35 | } 36 | 37 | .drop { 38 | border: 1px solid black; 39 | width: 300px; 40 | height: 300px; 41 | float: left; 42 | } 43 | -------------------------------------------------------------------------------- /05 User interactions/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | User interactin demo 10 | 11 | 12 | 13 |

Drag and drop

14 |
15 |
16 |
17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /05 User interactions/src/js/main.js: -------------------------------------------------------------------------------- 1 | document.onreadystatechange = () => { 2 | if (document.readyState === 'complete') { 3 | const dragStart = (event) => { 4 | event.dataTransfer.setData('text/plain', event.target.id); 5 | console.log('dragStart', event); 6 | }; 7 | 8 | const dragElement = document.getElementById('drag-element'); 9 | dragElement.addEventListener('dragstart', dragStart); 10 | 11 | const drop = (event) => { 12 | const id = event.dataTransfer.getData('text'); 13 | console.log('drop', id); 14 | event.target.appendChild(dragElement); 15 | }; 16 | 17 | const dragOver = (event) => { 18 | event.preventDefault(); 19 | event.dataTransfer.dropEffect = 'move'; 20 | console.log({ 21 | x: event.pageX, 22 | y: event.pageY 23 | }); 24 | }; 25 | 26 | const dropElementA = document.getElementById('drop-element-a'); 27 | const dropElementB = document.getElementById('drop-element-b'); 28 | 29 | dropElementA.addEventListener('dragover', dragOver); 30 | dropElementB.addEventListener('dragover', dragOver); 31 | 32 | dropElementA.addEventListener('drop', drop); 33 | dropElementB.addEventListener('drop', drop); 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /06 Source Timers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /06 Source Timers/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /06 Source Timers/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /06 Source Timers/src/js/main.js: -------------------------------------------------------------------------------- 1 | // Si lo dejamos así no aparece, ningún texto. ¿Por qué? La razón es que todavía 2 | // no se ha cargado el resto del documento, y que por tanto no existe el elemento 3 | // div con id = content. Evidenetemente, esto es porque esta, cargado en la head. 4 | // Lo que podemos hacer es un pequeño delay para que ese contenido este disponible. 5 | 6 | // document.getElementById('content').innerHTML = 'Main content from JS'; 7 | 8 | // Now works 9 | setTimeout(() => { 10 | document.getElementById('content').innerHTML = 'Main content from JS'; 11 | }, 100); 12 | 13 | // 14 | -------------------------------------------------------------------------------- /07 Timer Gotchas/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /07 Timer Gotchas/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /07 Timer Gotchas/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /07 Timer Gotchas/src/js/main.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | let repitions = 0; 3 | const totalRepetions = 1000; 4 | const requestDelay = 10; 5 | let totalDelay = 0; 6 | 7 | const testDelay = () => { 8 | if (repitions++ > totalRepetions) { 9 | const avarage = totalDelay / totalRepetions; 10 | alert(` 11 | Request delay: ${requestDelay}, 12 | Avarage delay: ${avarage} 13 | `); 14 | return; 15 | } 16 | 17 | const start = new Date(); 18 | setTimeout(() => { 19 | const delay = new Date() - start; 20 | totalDelay += delay; 21 | testDelay(); 22 | }, requestDelay); 23 | }; 24 | 25 | testDelay(); 26 | })(); 27 | -------------------------------------------------------------------------------- /08 Not always async/README.md: -------------------------------------------------------------------------------- 1 | ## Challange ¿Cuantos pequeños programas? 2 | * Folder structure: 3 | 4 | 08 Not Always Async/ 5 | ├── src/ 6 | │ ├── content/ 7 | | | ├── site.css 8 | │ ├── js/ 9 | | | ├── utils.js 10 | | | ├── main.js 11 | │ ├── index.html 12 | │ └── bootstrap-theme.min.css 13 | ├── gulpfile.js 14 | ├── package.json 15 | 16 | ## Steps. 17 | 18 | ### 1. Open `main.js`. 19 | 20 | * Challange -> How many little programs? // 1 21 | * What it's going to be show up on console? // 2, 4, 6 22 | 23 | * We can ask ourselves if this function is an async function or not. The fact is that there is no way to know it, without know how `forEach` function is implemented. 24 | 25 | * We can wrap the function with `setTimeout`, and we obtain another thing. 26 | -------------------------------------------------------------------------------- /08 Not always async/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /08 Not always async/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /08 Not always async/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /08 Not always async/src/js/main.js: -------------------------------------------------------------------------------- 1 | import { forEach } from './utils'; 2 | 3 | (() => { 4 | const numbers = [1, 2, 3]; 5 | console.log('start'); 6 | forEach(numbers, (number) => console.log(number * 2)); // async or not async 7 | console.log('end'); 8 | })(); 9 | -------------------------------------------------------------------------------- /08 Not always async/src/js/utils.js: -------------------------------------------------------------------------------- 1 | export const forEach = (items, callback) => { 2 | for (const item of items) { 3 | // callback(item); 4 | setTimeout(() => { 5 | callback(item); 6 | }, 0); 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /09 Web Workers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /09 Web Workers/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | 31 | progress { 32 | width: 80%; 33 | height: 20px; 34 | -webkit-appearance: none; 35 | } 36 | 37 | progress::-webkit-progress-bar { 38 | background: lightgrey; 39 | border-radius: 20px; 40 | border: 1px solid gray; 41 | } 42 | 43 | progress::-webkit-progress-value { 44 | background: #21F7F7; 45 | border-radius: 20px; 46 | } 47 | -------------------------------------------------------------------------------- /09 Web Workers/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /09 Web Workers/src/js/background.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const progress = document.getElementById('worker-progress'); 3 | 4 | const longTaskWorker = new Worker('./longTask.js'); 5 | 6 | longTaskWorker.onmessage = (message) => { 7 | progress.value = message.data; 8 | }; 9 | })(); 10 | -------------------------------------------------------------------------------- /09 Web Workers/src/js/inline.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const longTask = (progressCalback) => { 3 | for (let step = 0; step < 10; step++) { 4 | progressCalback(step * 10); 5 | /* 6 | Use the developer tools to show that the console log is not blocked 7 | */ 8 | console.log(step); 9 | const start = Date.now(); 10 | const seconds = 1; 11 | const waitUntil = start + seconds * 1000; 12 | while (Date.now() < waitUntil) {} 13 | } 14 | }; 15 | 16 | const progress = document.getElementById('worker-progress'); 17 | 18 | longTask((percentage) => { 19 | progress.value = percentage; 20 | }); 21 | })(); 22 | -------------------------------------------------------------------------------- /09 Web Workers/src/js/longTask.js: -------------------------------------------------------------------------------- 1 | const longTask = (progressCalback) => { 2 | for (let step = 0; step <= 10; step++) { 3 | progressCalback(step * 10); 4 | /* 5 | Use the developer tools to show that the console log is not blocked 6 | */ 7 | console.log(step); 8 | const start = Date.now(); 9 | const seconds = 1; 10 | const waitUntil = start + seconds * 1000; 11 | while (Date.now() < waitUntil) {} 12 | } 13 | }; 14 | 15 | longTask(postMessage); 16 | -------------------------------------------------------------------------------- /10 Event listeners are sync/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "startupmain", 3 | "version": "1.0.0", 4 | "description": "Demos start up point", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel ./src/index.html" 8 | }, 9 | "author": "Jaime Salas", 10 | "license": "ISC", 11 | "dependencies": { 12 | "parcel": "^1.10.3" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /10 Event listeners are sync/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /10 Event listeners are sync/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Document 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /10 Event listeners are sync/src/js/main.js: -------------------------------------------------------------------------------- 1 | (() => { 2 | const btn = document.getElementById('btn'); 3 | const logger = (message) => (evt) => { 4 | evt.stopPropagation(); 5 | console.log(message); 6 | }; 7 | btn.addEventListener('click', logger('first click')); 8 | btn.addEventListener('click', logger('second click')); 9 | 10 | setTimeout(() => { 11 | console.log('pre-click'); 12 | btn.click(); 13 | console.log('post-click'); 14 | }, 0); 15 | })(); 16 | // (function () { 17 | // const btn = document.getElementById('btn'); 18 | 19 | // btn.addEventListener('click', function () { 20 | // console.log('click-1'); 21 | // }); 22 | 23 | // btn.addEventListener('click', function () { 24 | // console.log('click-2'); 25 | // }); 26 | 27 | // setTimeout(function () { 28 | // console.log('pre-click'); 29 | // btn.click(); // De manera síncrona, llama a cada uno de los handlers. 30 | // console.log('post-click'); 31 | // }, 0); 32 | // })(); 33 | -------------------------------------------------------------------------------- /11 Axios/00_seed/README.md: -------------------------------------------------------------------------------- 1 | ## 0 Base 2 | 3 | ### 1. We are going to start our examples from here. For that purpose we have this folder structure 4 | 5 | 6 | |--src 7 | | |-- API 8 | | |-- content 9 | | |-- view 10 | |--|-- app.js 11 | |--index.html 12 | 13 | ### 2. We start from a mock service. And just verify we can render the list of books 14 | 15 | ```javascript src/API/bookAPI.js 16 | export const getBooks = () => { 17 | return [ 18 | { title: 'testA', author: 'testA', genre: 'fiction', read: true }, 19 | { title: 'testB', author: 'testB', genre: 'fiction', read: true } 20 | ]; 21 | }; 22 | ``` 23 | 24 | ### 3. The index html looks this way 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | Document 34 | 35 | 36 | 37 | 38 |
39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | ### 4. app.js has this look 47 | 48 | ```javascript 49 | import * as bookAPI from './API/bookAPI'; 50 | import { appendElement, createList } from './view/uiBuilder'; 51 | 52 | document.addEventListener('DOMContentLoaded', () => { 53 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 54 | buttonRetrieveBooks.addEventListener('click', (event) => { 55 | event.stopPropagation(); 56 | const books = bookAPI.getBooks() 57 | .map(b => `${b.title}, ${b.author}`); 58 | const bookList = createList(books); 59 | appendElement('books-container', bookList); 60 | }); 61 | }); 62 | ``` 63 | -------------------------------------------------------------------------------- /11 Axios/00_seed/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /11 Axios/00_seed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/00_seed/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | export const getBooks = () => { 2 | return [ 3 | { title: 'testA', author: 'testA', genre: 'fiction', read: true }, 4 | { title: 'testB', author: 'testB', genre: 'fiction', read: true } 5 | ]; 6 | }; -------------------------------------------------------------------------------- /11 Axios/00_seed/src/app.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from './API/bookAPI'; 2 | import { appendElement, createList } from './view/uiBuilder'; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 6 | buttonRetrieveBooks.addEventListener('click', (event) => { 7 | event.stopPropagation(); 8 | const books = bookAPI.getBooks() 9 | .map(b => `${b.title}, ${b.author}`); 10 | const bookList = createList(books); 11 | appendElement('books-container', bookList); 12 | }); 13 | }); -------------------------------------------------------------------------------- /11 Axios/00_seed/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/00_seed/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; -------------------------------------------------------------------------------- /11 Axios/01_get/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /11 Axios/01_get/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.12.4" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/01_get/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBooks = () => { 5 | axios.get('http://localhost/api/books') 6 | .then((result) => { 7 | let titles = []; 8 | result.data.forEach((item) => { 9 | titles.push(item.title); 10 | }); 11 | const list = createList(titles); 12 | appendElement('books-container', list); 13 | }) 14 | .catch((err) => console.log(err)); 15 | }; -------------------------------------------------------------------------------- /11 Axios/01_get/src/app.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from './API/bookAPI'; 2 | import { appendElement, createList } from './view/uiBuilder'; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 6 | buttonRetrieveBooks.addEventListener('click', (event) => { 7 | event.stopPropagation(); 8 | bookAPI.getBooks(); 9 | }); 10 | }); -------------------------------------------------------------------------------- /11 Axios/01_get/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/01_get/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; -------------------------------------------------------------------------------- /11 Axios/02_post/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 |
19 | Title:
20 | 21 |
22 | Genre:
23 | 24 |
25 | Author:
26 | 27 |
28 | Have you read this book?:
29 | 30 |

31 | 32 |
33 |
34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /11 Axios/02_post/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/02_post/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | const errorHandler = (err) => console.log(err); 5 | 6 | export const getBooks = () => { 7 | axios.get('http://localhost:8000/api/books') 8 | .then((result) => { 9 | let titles = []; 10 | result.data.forEach((item) => { 11 | titles.push(item.title); 12 | }); 13 | const list = createList(titles); 14 | appendElement('books-container', list); 15 | }) 16 | .catch(errorHandler); 17 | }; 18 | 19 | export const postBook = (book) => { 20 | axios.post('http://localhost:8000/api/books', book) 21 | .then(() => getBooks()) 22 | .catch(errorHandler); 23 | }; -------------------------------------------------------------------------------- /11 Axios/02_post/src/app.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from './API/bookAPI'; 2 | import { appendElement, createList } from './view/uiBuilder'; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 6 | buttonRetrieveBooks.addEventListener('click', (event) => { 7 | event.stopPropagation(); 8 | bookAPI.getBooks(); 9 | }); 10 | const conatinerAddBookFormSubmit = document.getElementById('container-add-book-form-submit'); 11 | conatinerAddBookFormSubmit.addEventListener('click', (event) => { 12 | event.preventDefault(); 13 | event.stopPropagation(); 14 | const book = { 15 | title: document.getElementById('title').value, 16 | author: document.getElementById('author').value, 17 | genre: document.getElementById('genre').value, 18 | read: document.getElementById('read').checked 19 | }; 20 | bookAPI.postBook(book); 21 | }); 22 | }); -------------------------------------------------------------------------------- /11 Axios/02_post/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/02_post/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; -------------------------------------------------------------------------------- /11 Axios/03_callbacks/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /11 Axios/03_callbacks/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 |
18 | Title:
19 | 20 |
21 | Genre:
22 | 23 |
24 | Author:
25 | 26 |
27 | Have you read this book?:
28 | 29 |

30 | 31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /11 Axios/03_callbacks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/core": "^7.8.4", 15 | "@babel/plugin-proposal-class-properties": "^7.8.3", 16 | "parcel": "^1.12.4" 17 | }, 18 | "dependencies": { 19 | "axios": "^0.18.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /11 Axios/03_callbacks/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBooks = (err, success) => { 5 | axios.get('http://localhost:8000/api/books') 6 | .then(success) 7 | .catch(err); 8 | }; 9 | 10 | export const postBook = (book) => (err, success) => { 11 | axios.post('http://localhost:8000/api/books', book) 12 | .then(success) 13 | .catch(err); 14 | }; -------------------------------------------------------------------------------- /11 Axios/03_callbacks/src/app.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from './API/bookAPI'; 2 | import { appendElement, createList } from './view/uiBuilder'; 3 | 4 | const onSuccess = (result) => { 5 | let titles = []; 6 | // Remove data to watch how error is handle. 7 | result.data.forEach((item) => { 8 | titles.push(item.title); 9 | }); 10 | const list = createList(titles); 11 | appendElement('books-container', list); 12 | }; 13 | 14 | const onError = (error) => console.log(error); 15 | 16 | document.addEventListener('DOMContentLoaded', () => { 17 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 18 | buttonRetrieveBooks.addEventListener('click', (event) => { 19 | event.stopPropagation(); 20 | // bookAPI.getBooks(); 21 | bookAPI.getBooks(onError, onSuccess); 22 | }); 23 | const conatinerAddBookFormSubmit = document.getElementById('container-add-book-form-submit'); 24 | conatinerAddBookFormSubmit.addEventListener('click', (event) => { 25 | event.preventDefault(); 26 | event.stopPropagation(); 27 | const book = { 28 | title: document.getElementById('title').value, 29 | author: document.getElementById('author').value, 30 | genre: document.getElementById('genre').value, 31 | read: document.getElementById('read').checked 32 | }; 33 | // bookAPI.postBook(book); 34 | bookAPI.postBook(book) 35 | ( 36 | onError, 37 | (result) => bookAPI.getBooks(onError, onSuccess) 38 | ); 39 | }); 40 | }); -------------------------------------------------------------------------------- /11 Axios/03_callbacks/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/03_callbacks/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; -------------------------------------------------------------------------------- /11 Axios/04_promises/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /11 Axios/04_promises/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 |
18 | Title:
19 | 20 |
21 | Genre:
22 | 23 |
24 | Author:
25 | 26 |
27 | Have you read this book?:
28 | 29 |

30 | 31 |
32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /11 Axios/04_promises/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/core": "^7.8.4", 15 | "@babel/plugin-proposal-class-properties": "^7.8.3", 16 | "parcel": "^1.12.4" 17 | }, 18 | "dependencies": { 19 | "axios": "^0.18.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /11 Axios/04_promises/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBooks = () => ( 5 | axios.get('http://localhost:8000/api/books') 6 | ); 7 | 8 | export const postBook = (book) => ( 9 | axios.post('http://localhost:8000/api/books', book) 10 | ); -------------------------------------------------------------------------------- /11 Axios/04_promises/src/app.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from './API/bookAPI'; 2 | import { appendElement, createList } from './view/uiBuilder'; 3 | 4 | const onSuccess = (result) => { 5 | let titles = []; 6 | // Remove data to watch how error is handle. 7 | result.data.forEach((item) => { 8 | titles.push(item.title); 9 | }); 10 | const list = createList(titles); 11 | appendElement('books-container', list); 12 | }; 13 | 14 | const onError = (error) => console.log(error); 15 | 16 | document.addEventListener('DOMContentLoaded', () => { 17 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 18 | buttonRetrieveBooks.addEventListener('click', (event) => { 19 | event.stopPropagation(); 20 | // bookAPI.getBooks(onError, onSuccess); 21 | bookAPI.getBooks() 22 | .then(onSuccess) 23 | .catch(onError); 24 | }); 25 | const conatinerAddBookFormSubmit = document.getElementById('container-add-book-form-submit'); 26 | conatinerAddBookFormSubmit.addEventListener('click', (event) => { 27 | event.preventDefault(); 28 | event.stopPropagation(); 29 | const book = { 30 | title: document.getElementById('title').value, 31 | author: document.getElementById('author').value, 32 | genre: document.getElementById('genre').value, 33 | read: document.getElementById('read').checked 34 | }; 35 | // bookAPI.postBook(book) 36 | // ( 37 | // onError, 38 | // (result) => bookAPI.getBooks(onError, onSuccess) 39 | // ); 40 | bookAPI.postBook(book) 41 | .then(() => bookAPI.getBooks()) 42 | .then(onSuccess) 43 | .catch(onError); 44 | }); 45 | }); -------------------------------------------------------------------------------- /11 Axios/04_promises/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/04_promises/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; -------------------------------------------------------------------------------- /11 Axios/05_all/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /11 Axios/05_all/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 |
22 |
23 | Title:
24 | 25 |
26 | Genre:
27 | 28 |
29 | Author:
30 | 31 |
32 | Have you read this book?:
33 | 34 |

35 | 36 |
37 |
38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /11 Axios/05_all/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/core": "^7.8.4", 15 | "@babel/plugin-proposal-class-properties": "^7.8.3", 16 | "parcel": "^1.12.4" 17 | }, 18 | "dependencies": { 19 | "axios": "^0.18.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /11 Axios/05_all/src/API/authorAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getAuthors = () => ( 4 | axios.get('http://localhost:8000/api/authors') 5 | ); -------------------------------------------------------------------------------- /11 Axios/05_all/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBooks = (err, success) => ( 5 | axios.get('http://localhost:8000/api/books') 6 | ); 7 | 8 | export const postBook = (book) => (err, success) => ( 9 | axios.post('http://localhost:8000/api/books', book) 10 | ); -------------------------------------------------------------------------------- /11 Axios/05_all/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/05_all/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 | Title:
22 | 23 |
24 | Genre:
25 | 26 |
27 | Author:
28 | 29 |
30 | Have you read this book?:
31 | 32 |

33 | 34 |
35 |
36 | Book search 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "@babel/core": "^7.8.4", 15 | "@babel/plugin-proposal-class-properties": "^7.8.3", 16 | "parcel": "^1.12.4" 17 | }, 18 | "dependencies": { 19 | "axios": "^0.18.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/src/API/authorAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getAuthors = () => ( 4 | axios.get('http://localhost:8000/api/authors') 5 | ); -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBooks = () => ( 5 | axios.get('http://localhost:8000/api/books') 6 | ); 7 | 8 | export const getBooksByParams = (params) => ( 9 | axios.get('http://localhost:8000/api/books', params) 10 | ); 11 | 12 | export const postBook = (book) => () => ( 13 | axios.post('http://localhost:8000/api/books', book) 14 | ); -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/src/pages/booksearch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

Book search

12 |
13 |
14 | Title:
15 | 16 |
17 | 18 | Genre:
19 | 20 |
21 | 22 | Author:
23 | 24 |
25 | 26 | 28 |
29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/src/pages/booksearch/main.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from '../../API/bookAPI'; 2 | import { appendElement, createList } from '../../view/uiBuilder'; 3 | 4 | const onSuccess = (result, container) => { 5 | let titles = []; 6 | // Remove data to watch how error is handle. 7 | result.data.forEach((item) => { 8 | titles.push(item.title); 9 | }); 10 | const list = createList(titles); 11 | appendElement(container, list); 12 | }; 13 | 14 | const onError = (error) => console.log(error); 15 | 16 | const parseParams = () => { 17 | let obj = { params: {} }; 18 | 19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value; 20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value; 21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value; 22 | 23 | return obj; 24 | }; 25 | 26 | document.addEventListener('DOMContentLoaded', () => { 27 | const buttonSearchBooks = document.getElementById('button-search-books'); 28 | buttonSearchBooks.addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | bookAPI.getBooksByParams(parseParams()) 31 | .then((result) => onSuccess(result, 'books-container')) 32 | .catch(onError); 33 | }); 34 | }); -------------------------------------------------------------------------------- /11 Axios/06_multiple_params/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; -------------------------------------------------------------------------------- /11 Axios/07_interceptors/README_99.MD: -------------------------------------------------------------------------------- 1 | ## We're going to create a new page that will contain a login form, if we're authenticated, the response will return a JWT, that we will use to call an API. 2 | 3 | -------------------------------------------------------------------------------- /11 Axios/07_interceptors/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 | Title:
22 | 23 |
24 | Genre:
25 | 26 |
27 | Author:
28 | 29 |
30 | Have you read this book?:
31 | 32 |

33 | 34 |
35 |
36 | Book search 37 | Access 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /11 Axios/07_interceptors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/API/accessAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import setUp from './interceptors'; 3 | 4 | export const grantAccess = () => ( 5 | axios.get('http://localhost:8000/api/access') 6 | ); -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/API/authorAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getAuthors = () => ( 4 | axios.get('http://localhost:8000/api/authors') 5 | ); -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBooks = () => ( 5 | axios.get('http://localhost:8000/api/books') 6 | ); 7 | 8 | export const getBooksByParams = (params) => ( 9 | axios.get('http://localhost:8000/api/books', params) 10 | ); 11 | 12 | export const postBook = (book) => () => ( 13 | axios.post('http://localhost:8000/api/books', book) 14 | ); -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/API/interceptors.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const setUp = () => { 4 | axios.interceptors.request.use((config) => { 5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods' 6 | return config; 7 | }, (err) => { 8 | return Promise.reject(err); 9 | }); 10 | 11 | axios.interceptors.response.use((response) => { 12 | // Do something with data 13 | console.log(response); 14 | // TODO: Catch response. Simulate on server different status 15 | return response; 16 | }, (error) => { 17 | console.log(error.response.status); 18 | console.log(error.response.statusText); 19 | window.location = "/index.html"; // Play with both 20 | //return Promise.reject(error.response); 21 | }); 22 | }; 23 | 24 | export default setUp(); -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/pages/access.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LEMONCODE 16/17 Async JavaScript jQuery 8 | 9 | 10 | 11 | 12 | 13 |

Access page

14 |

The access is random so good luck!!

15 |
16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/pages/access/main.js: -------------------------------------------------------------------------------- 1 | import * as accessAPI from '../../API/accessAPI'; 2 | import {createTextEntry, appendElement} from '../../view/uiBuilder'; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | console.log('hello main') 6 | const buttonAccess = document.getElementById('button-access'); 7 | buttonAccess.addEventListener('click', (event) => { 8 | event.stopPropagation(); 9 | accessAPI.grantAccess() 10 | .then((result) => { 11 | const text = createTextEntry(result.data.message); 12 | appendElement('container', text); 13 | }) 14 | .catch((err) => console.log(err)); 15 | }); 16 | }); -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/pages/booksearch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

Book search

12 |
13 |
14 | Title:
15 | 16 |
17 | 18 | Genre:
19 | 20 |
21 | 22 | Author:
23 | 24 |
25 | 26 | 28 |
29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/pages/booksearch/main.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from '../../API/bookAPI'; 2 | import { appendElement, createList } from '../../view/uiBuilder'; 3 | 4 | const onSuccess = (result, container) => { 5 | let titles = []; 6 | // Remove data to watch how error is handle. 7 | result.data.forEach((item) => { 8 | titles.push(item.title); 9 | }); 10 | const list = createList(titles); 11 | appendElement(container, list); 12 | }; 13 | 14 | const onError = (error) => console.log(error); 15 | 16 | const parseParams = () => { 17 | let obj = { params: {} }; 18 | 19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value; 20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value; 21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value; 22 | 23 | return obj; 24 | }; 25 | 26 | document.addEventListener('DOMContentLoaded', () => { 27 | const buttonSearchBooks = document.getElementById('button-search-books'); 28 | buttonSearchBooks.addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | bookAPI.getBooksByParams(parseParams()) 31 | .then((result) => onSuccess(result, 'books-container')) 32 | .catch(onError); 33 | }); 34 | }); -------------------------------------------------------------------------------- /11 Axios/07_interceptors/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; 15 | 16 | export const createTextEntry = (text) => { 17 | let textEntry = document.createElement('p'); 18 | textEntry.appendChild(document.createTextNode(text)); 19 | return textEntry; 20 | }; -------------------------------------------------------------------------------- /11 Axios/07b_interceptors/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Axios Interceptors example 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /11 Axios/07b_interceptors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/07b_interceptors/src/API/interceptors.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | // const setUp = () => { 4 | // axios.interceptors.request.use((config) => { 5 | // config.headers['GEN_TOKEN'] = '000aks1243545iopods' 6 | // return config; 7 | // }, (err) => { 8 | // return Promise.reject(err); 9 | // }); 10 | 11 | // axios.interceptors.response.use((response) => { 12 | // // Do something with data 13 | // console.log(response); 14 | // // TODO: Catch response. Simulate on server different status 15 | // return response; 16 | // }, (error) => { 17 | // console.log(error.response.status); 18 | // console.log(error.response.statusText); 19 | // window.location = "/index.html"; // Play with both 20 | // //return Promise.reject(error.response); 21 | // }); 22 | // }; 23 | 24 | // export default setUp(); 25 | 26 | export const setUpRequest = (token) => { 27 | axios.interceptors.request.use((config) => { 28 | config.headers['Authorization'] = `Bearer ${token}`; 29 | return config; 30 | }, (err) => { 31 | return Promise.reject(err); 32 | }); 33 | }; -------------------------------------------------------------------------------- /11 Axios/07b_interceptors/src/API/loginAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export default ({username, password}) => ( 4 | axios.post('http://localhost:8887/login', {username, password}) 5 | ); -------------------------------------------------------------------------------- /11 Axios/07b_interceptors/src/app.js: -------------------------------------------------------------------------------- 1 | import { submitButtonPromise } from './asyncApi'; 2 | import login from './API/loginAPI'; 3 | import { setUpRequest } from './API/interceptors'; 4 | import axios from 'axios'; 5 | 6 | const readCredentials = () => { 7 | const username = document.getElementById('username').value; 8 | const password = document.getElementById('password').value; 9 | return { 10 | username, 11 | password, 12 | }; 13 | }; 14 | 15 | document.addEventListener('DOMContentLoaded', () => { 16 | submitButtonPromise('click', 'login') 17 | .then(() => { 18 | const credentials = readCredentials(); 19 | return login(credentials) 20 | }) 21 | .then((result) => { 22 | const { access_token } = result.data; 23 | setUpRequest(access_token); 24 | }) 25 | .catch((err) => console.log(err)); 26 | 27 | document.getElementById('cars') 28 | .addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | axios.get('http://localhost:3050/api/cars') 31 | .then((result) => console.log(result)) 32 | .catch((err) => console.log(err)); 33 | }); 34 | }); -------------------------------------------------------------------------------- /11 Axios/07b_interceptors/src/asyncApi.js: -------------------------------------------------------------------------------- 1 | export const submitButtonPromise = (event, targetId) => ( 2 | new Promise((resolve, _) => { 3 | document.getElementById(targetId) 4 | .addEventListener(event, (evt) => { 5 | evt.stopPropagation(); 6 | evt.preventDefault(); 7 | resolve(); 8 | }); 9 | }) 10 | ); -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 | Title:
22 | 23 |
24 | Genre:
25 | 26 |
27 | Author:
28 | 29 |
30 | Have you read this book?:
31 | 32 |

33 | 34 |
35 |
36 | Book search 37 | Access 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/API/accessAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import setUp from './interceptors'; 3 | 4 | export const grantAccess = () => ( 5 | axios.get('http://localhost:8000/api/access') 6 | ); -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/API/authorAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getAuthors = () => ( 4 | axios.get('http://localhost:8000/api/authors') 5 | ); -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | // export const getBooks = () => ( 5 | // axios.get('http://localhost:8000/api/books') 6 | // ); 7 | 8 | export const getBooks = (cancelSettings) => ( 9 | axios.get('http://localhost:8000/api/books', cancelSettings) 10 | ); 11 | 12 | export const getBooksByParams = (params) => ( 13 | axios.get('http://localhost:8000/api/books', params) 14 | ); 15 | 16 | export const postBook = (book) => () => ( 17 | axios.post('http://localhost:8000/api/books', book) 18 | ); -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/API/interceptors.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const setUp = () => { 4 | axios.interceptors.request.use((config) => { 5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods' 6 | return config; 7 | }, (err) => { 8 | return Promise.reject(err); 9 | }); 10 | 11 | axios.interceptors.response.use((response) => { 12 | // Do something with data 13 | console.log(response); 14 | // TODO: Catch response. Simulate on server different status 15 | return response; 16 | }, (error) => { 17 | console.log(error.response.status); 18 | console.log(error.response.statusText); 19 | window.location = "/index.html"; // Play with both 20 | //return Promise.reject(error.response); 21 | }); 22 | }; 23 | 24 | export default setUp(); -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/pages/access.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LEMONCODE 16/17 Async JavaScript jQuery 8 | 9 | 10 | 11 | 12 | 13 |

Access page

14 |

The access is random so good luck!!

15 |
16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/pages/access/main.js: -------------------------------------------------------------------------------- 1 | import * as accessAPI from '../../API/accessAPI'; 2 | import {createTextEntry, appendElement} from '../../view/uiBuilder'; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | console.log('hello main') 6 | const buttonAccess = document.getElementById('button-access'); 7 | buttonAccess.addEventListener('click', (event) => { 8 | event.stopPropagation(); 9 | accessAPI.grantAccess() 10 | .then((result) => { 11 | const text = createTextEntry(result.data.message); 12 | appendElement('container', text); 13 | }) 14 | .catch((err) => console.log(err)); 15 | }); 16 | }); -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/pages/booksearch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

Book search

12 |
13 |
14 | Title:
15 | 16 |
17 | 18 | Genre:
19 | 20 |
21 | 22 | Author:
23 | 24 |
25 | 26 | 28 |
29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/pages/booksearch/main.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from '../../API/bookAPI'; 2 | import { appendElement, createList } from '../../view/uiBuilder'; 3 | 4 | const onSuccess = (result, container) => { 5 | let titles = []; 6 | // Remove data to watch how error is handle. 7 | result.data.forEach((item) => { 8 | titles.push(item.title); 9 | }); 10 | const list = createList(titles); 11 | appendElement(container, list); 12 | }; 13 | 14 | const onError = (error) => console.log(error); 15 | 16 | const parseParams = () => { 17 | let obj = { params: {} }; 18 | 19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value; 20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value; 21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value; 22 | 23 | return obj; 24 | }; 25 | 26 | document.addEventListener('DOMContentLoaded', () => { 27 | const buttonSearchBooks = document.getElementById('button-search-books'); 28 | buttonSearchBooks.addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | bookAPI.getBooksByParams(parseParams()) 31 | .then((result) => onSuccess(result, 'books-container')) 32 | .catch(onError); 33 | }); 34 | }); -------------------------------------------------------------------------------- /11 Axios/08_cancel_token/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; 15 | 16 | export const createTextEntry = (text) => { 17 | let textEntry = document.createElement('p'); 18 | textEntry.appendChild(document.createTextNode(text)); 19 | return textEntry; 20 | }; -------------------------------------------------------------------------------- /11 Axios/08b_cancel_token/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Axios Interceptors example 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 | 23 |
24 |
25 |
26 | 27 |
28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /11 Axios/08b_cancel_token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/08b_cancel_token/src/API/interceptors.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const setUpRequest = (token) => { 4 | axios.interceptors.request.use((config) => { 5 | config.headers['Authorization'] = `Bearer ${token}`; 6 | return config; 7 | }, (err) => { 8 | return Promise.reject(err); 9 | }); 10 | }; 11 | 12 | export const setUpResponse = () => { 13 | axios.interceptors.response.use((response) => { 14 | // Do something with data 15 | console.log(response); 16 | return response; 17 | }, (error) => { 18 | if (err.response && error.response.status === 401) { 19 | console.log('you are not allowed'); 20 | } 21 | return Promise.reject('I am canceled'); 22 | }); 23 | } -------------------------------------------------------------------------------- /11 Axios/08b_cancel_token/src/API/loginAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export default ({username, password}) => ( 4 | axios.post('http://localhost:8887/login', {username, password}) 5 | ); -------------------------------------------------------------------------------- /11 Axios/08b_cancel_token/src/app.js: -------------------------------------------------------------------------------- 1 | import { submitButtonPromise } from './asyncApi'; 2 | import login from './API/loginAPI'; 3 | import { setUpRequest, setUpResponse } from './API/interceptors'; 4 | import axios from 'axios'; 5 | 6 | const readCredentials = () => { 7 | const username = document.getElementById('username').value; 8 | const password = document.getElementById('password').value; 9 | return { 10 | username, 11 | password, 12 | }; 13 | }; 14 | 15 | const getCars = (cancelSettings) => ( 16 | axios.get('http://localhost:3050/api/cars', cancelSettings) 17 | ); 18 | 19 | document.addEventListener('DOMContentLoaded', () => { 20 | setUpResponse(); 21 | submitButtonPromise('click', 'login') 22 | .then(() => { 23 | const credentials = readCredentials(); 24 | return login(credentials) 25 | }) 26 | .then((result) => { 27 | const { access_token } = result.data; 28 | setUpRequest(access_token); 29 | }) 30 | .catch((err) => console.log(err)); 31 | 32 | document.getElementById('cars') 33 | .addEventListener('click', (event) => { 34 | event.stopPropagation(); 35 | const CancelToken = axios.CancelToken; 36 | let source = CancelToken.source(); 37 | getCars({ cancelToken: source.token }) 38 | .then((result) => console.log(result)) 39 | .catch((err) => console.log(err)); 40 | 41 | source.cancel(); 42 | }); 43 | }); -------------------------------------------------------------------------------- /11 Axios/08b_cancel_token/src/asyncApi.js: -------------------------------------------------------------------------------- 1 | export const submitButtonPromise = (event, targetId) => ( 2 | new Promise((resolve, _) => { 3 | document.getElementById(targetId) 4 | .addEventListener(event, (evt) => { 5 | evt.stopPropagation(); 6 | evt.preventDefault(); 7 | resolve(); 8 | }); 9 | }) 10 | ); -------------------------------------------------------------------------------- /11 Axios/09_patch/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 | Title:
22 | 23 |
24 | Genre:
25 | 26 |
27 | Author:
28 | 29 |
30 | Have you read this book?:
31 | 32 |

33 | 34 |
35 |
36 | Book search 37 | Book patch 38 | Access 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /11 Axios/09_patch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/09_patch/src/API/accessAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import setUp from './interceptors'; 3 | 4 | export const grantAccess = () => ( 5 | axios.get('http://localhost:8000/api/access') 6 | ); -------------------------------------------------------------------------------- /11 Axios/09_patch/src/API/authorAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getAuthors = () => ( 4 | axios.get('http://localhost:8000/api/authors') 5 | ); -------------------------------------------------------------------------------- /11 Axios/09_patch/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBookById = (bookId) => ( 5 | axios.get(`http://localhost:8000/api/books/${bookId}`) 6 | ); 7 | 8 | export const getBooks = (cancelSettings) => ( 9 | axios.get('http://localhost:8000/api/books', cancelSettings) 10 | ); 11 | 12 | export const getBooksByParams = (params) => ( 13 | axios.get('http://localhost:8000/api/books', params) 14 | ); 15 | 16 | export const postBook = (book) => ( 17 | axios.post('http://localhost:8000/api/books', book) 18 | ); 19 | 20 | export const patchBook = (bookId, book) => ( 21 | axios.patch(`http://localhost:8000/api/books/${bookId}`, book) 22 | ); -------------------------------------------------------------------------------- /11 Axios/09_patch/src/API/interceptors.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const setUp = () => { 4 | axios.interceptors.request.use((config) => { 5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods' 6 | return config; 7 | }, (err) => { 8 | return Promise.reject(err); 9 | }); 10 | 11 | axios.interceptors.response.use((response) => { 12 | // Do something with data 13 | console.log(response); 14 | // TODO: Catch response. Simulate on server different status 15 | return response; 16 | }, (error) => { 17 | console.log(error.response.status); 18 | console.log(error.response.statusText); 19 | window.location = "/index.html"; // Play with both 20 | //return Promise.reject(error.response); 21 | }); 22 | }; 23 | 24 | export default setUp(); -------------------------------------------------------------------------------- /11 Axios/09_patch/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/09_patch/src/pages/access.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LEMONCODE 16/17 Async JavaScript jQuery 8 | 9 | 10 | 11 | 12 | 13 |

Access page

14 |

The access is random so good luck!!

15 |
16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /11 Axios/09_patch/src/pages/access/main.js: -------------------------------------------------------------------------------- 1 | import * as accessAPI from '../../API/accessAPI'; 2 | import {createTextEntry, appendElement} from '../../view/uiBuilder'; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | const buttonAccess = document.getElementById('button-access'); 6 | buttonAccess.addEventListener('click', (event) => { 7 | event.stopPropagation(); 8 | accessAPI.grantAccess() 9 | .then((result) => { 10 | const text = createTextEntry(result.data.message); 11 | appendElement('container', text); 12 | }) 13 | .catch((err) => console.log(err)); 14 | }); 15 | }); -------------------------------------------------------------------------------- /11 Axios/09_patch/src/pages/bookpatch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

Book patch

12 |
13 |
14 | Id:
15 | 16 |
17 | 18 | Title:
19 | 20 |
21 | 22 | Genre:
23 | 24 |
25 | 26 | Author:
27 | 28 |
29 | 30 | Have you read this book?:
31 | 32 | 33 |
34 |
35 |
36 |

Modified book

37 |
38 | 39 | 40 | -------------------------------------------------------------------------------- /11 Axios/09_patch/src/pages/booksearch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

Book search

12 |
13 |
14 | Title:
15 | 16 |
17 | 18 | Genre:
19 | 20 |
21 | 22 | Author:
23 | 24 |
25 | 26 | 28 |
29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /11 Axios/09_patch/src/pages/booksearch/main.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from '../../API/bookAPI'; 2 | import { appendElement, createList } from '../../view/uiBuilder'; 3 | 4 | const onSuccess = (result, container) => { 5 | let titles = []; 6 | // Remove data to watch how error is handle. 7 | result.data.forEach((item) => { 8 | titles.push(item.title); 9 | }); 10 | const list = createList(titles); 11 | appendElement(container, list); 12 | }; 13 | 14 | const onError = (error) => console.log(error); 15 | 16 | const parseParams = () => { 17 | let obj = { params: {} }; 18 | 19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value; 20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value; 21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value; 22 | 23 | return obj; 24 | }; 25 | 26 | document.addEventListener('DOMContentLoaded', () => { 27 | const buttonSearchBooks = document.getElementById('button-search-books'); 28 | buttonSearchBooks.addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | bookAPI.getBooksByParams(parseParams()) 31 | .then((result) => onSuccess(result, 'books-container')) 32 | .catch(onError); 33 | }); 34 | }); -------------------------------------------------------------------------------- /11 Axios/09_patch/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; 15 | 16 | export const createTextEntry = (text) => { 17 | let textEntry = document.createElement('p'); 18 | textEntry.appendChild(document.createTextNode(text)); 19 | return textEntry; 20 | }; 21 | 22 | export const createElement = (element) => { 23 | const div = document.createElement('div'); 24 | 25 | for (const key in element) { 26 | if (element.hasOwnProperty(key) && key !== '_id' && key !== '__v') { 27 | const value = element[key]; 28 | let p = document.createElement('p'); 29 | p.appendChild(document.createTextNode(`${key}: ${value}`)); 30 | div.appendChild(p); 31 | } 32 | } 33 | 34 | return div; 35 | } 36 | 37 | export const cleanElement = (target) => document.getElementById(target).innerHTML = ''; -------------------------------------------------------------------------------- /11 Axios/10_delete/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Document 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 | Title:
22 | 23 |
24 | Genre:
25 | 26 |
27 | Author:
28 | 29 |
30 | Have you read this book?:
31 | 32 |

33 | 34 |
35 |
36 | Book search 37 | Book patch 38 | Access 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /11 Axios/10_delete/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_get_started", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "parcel": "^1.8.1" 15 | }, 16 | "dependencies": { 17 | "axios": "^0.18.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /11 Axios/10_delete/src/API/accessAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import setUp from './interceptors'; 3 | 4 | export const grantAccess = () => ( 5 | axios.get('http://localhost:8000/api/access') 6 | ); -------------------------------------------------------------------------------- /11 Axios/10_delete/src/API/authorAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | export const getAuthors = () => ( 4 | axios.get('http://localhost:8000/api/authors') 5 | ); -------------------------------------------------------------------------------- /11 Axios/10_delete/src/API/bookAPI.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { appendElement, createList } from '../view/uiBuilder'; 3 | 4 | export const getBookById = (bookId) => ( 5 | axios.get(`http://localhost:8000/api/books/${bookId}`) 6 | ); 7 | 8 | export const getBooks = (cancelSettings) => ( 9 | axios.get('http://localhost:8000/api/books', cancelSettings) 10 | ); 11 | 12 | export const getBooksByParams = (params) => ( 13 | axios.get('http://localhost:8000/api/books', params) 14 | ); 15 | 16 | export const postBook = (book) => ( 17 | axios.post('http://localhost:8000/api/books', book) 18 | ); 19 | 20 | export const patchBook = (bookId, book) => ( 21 | axios.patch(`http://localhost:8000/api/books/${bookId}`, book) 22 | ); 23 | 24 | export const deleteBookById = (bookId) => ( 25 | axios.delete(`http://localhost:8000/api/books/${bookId}`) 26 | ); 27 | -------------------------------------------------------------------------------- /11 Axios/10_delete/src/API/interceptors.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | const setUp = () => { 4 | axios.interceptors.request.use((config) => { 5 | config.headers['GEN_TOKEN'] = '000aks1243545iopods' 6 | return config; 7 | }, (err) => { 8 | return Promise.reject(err); 9 | }); 10 | 11 | axios.interceptors.response.use((response) => { 12 | // Do something with data 13 | console.log(response); 14 | // TODO: Catch response. Simulate on server different status 15 | return response; 16 | }, (error) => { 17 | console.log(error.response.status); 18 | console.log(error.response.statusText); 19 | window.location = "/index.html"; // Play with both 20 | //return Promise.reject(error.response); 21 | }); 22 | }; 23 | 24 | export default setUp(); -------------------------------------------------------------------------------- /11 Axios/10_delete/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /11 Axios/10_delete/src/pages/access.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LEMONCODE 16/17 Async JavaScript jQuery 8 | 9 | 10 | 11 | 12 | 13 |

Access page

14 |

The access is random so good luck!!

15 |
16 | 17 |
18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /11 Axios/10_delete/src/pages/access/main.js: -------------------------------------------------------------------------------- 1 | import * as accessAPI from '../../API/accessAPI'; 2 | import {createTextEntry, appendElement} from '../../view/uiBuilder'; 3 | 4 | document.addEventListener('DOMContentLoaded', () => { 5 | const buttonAccess = document.getElementById('button-access'); 6 | buttonAccess.addEventListener('click', (event) => { 7 | event.stopPropagation(); 8 | accessAPI.grantAccess() 9 | .then((result) => { 10 | const text = createTextEntry(result.data.message); 11 | appendElement('container', text); 12 | }) 13 | .catch((err) => console.log(err)); 14 | }); 15 | }); -------------------------------------------------------------------------------- /11 Axios/10_delete/src/pages/bookpatch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

Book patch

12 |
13 |
14 | Id:
15 | 16 |
17 | 18 | Title:
19 | 20 |
21 | 22 | Genre:
23 | 24 |
25 | 26 | Author:
27 | 28 |
29 | 30 | Have you read this book?:
31 | 32 | 33 | 34 |
35 |
36 |
37 |

Modified book

38 |
39 | 40 | 41 | -------------------------------------------------------------------------------- /11 Axios/10_delete/src/pages/booksearch.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

Book search

12 |
13 |
14 | Title:
15 | 16 |
17 | 18 | Genre:
19 | 20 |
21 | 22 | Author:
23 | 24 |
25 | 26 | 28 |
29 |
30 |
31 | 32 |
33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /11 Axios/10_delete/src/pages/booksearch/main.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from '../../API/bookAPI'; 2 | import { appendElement, createList } from '../../view/uiBuilder'; 3 | 4 | const onSuccess = (result, container) => { 5 | let titles = []; 6 | // Remove data to watch how error is handle. 7 | result.data.forEach((item) => { 8 | titles.push(item.title); 9 | }); 10 | const list = createList(titles); 11 | appendElement(container, list); 12 | }; 13 | 14 | const onError = (error) => console.log(error); 15 | 16 | const parseParams = () => { 17 | let obj = { params: {} }; 18 | 19 | if(document.getElementById('title').value !== '') obj.params.title = document.getElementById('title').value; 20 | if(document.getElementById('author').value !== '') obj.params.author = document.getElementById('author').value; 21 | if(document.getElementById('genre').value !== '') obj.params.genre = document.getElementById('genre').value; 22 | 23 | return obj; 24 | }; 25 | 26 | document.addEventListener('DOMContentLoaded', () => { 27 | const buttonSearchBooks = document.getElementById('button-search-books'); 28 | buttonSearchBooks.addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | bookAPI.getBooksByParams(parseParams()) 31 | .then((result) => onSuccess(result, 'books-container')) 32 | .catch(onError); 33 | }); 34 | }); -------------------------------------------------------------------------------- /11 Axios/10_delete/src/view/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 7 | list.appendChild(listItem); 8 | }); 9 | return list; 10 | }; 11 | 12 | export const appendElement = (target, item) => { 13 | document.getElementById(target).appendChild(item); 14 | }; 15 | 16 | export const createTextEntry = (text) => { 17 | let textEntry = document.createElement('p'); 18 | textEntry.appendChild(document.createTextNode(text)); 19 | return textEntry; 20 | }; 21 | 22 | export const createElement = (element) => { 23 | const div = document.createElement('div'); 24 | 25 | for (const key in element) { 26 | if (element.hasOwnProperty(key) && key !== '_id' && key !== '__v') { 27 | const value = element[key]; 28 | let p = document.createElement('p'); 29 | p.appendChild(document.createTextNode(`${key}: ${value}`)); 30 | div.appendChild(p); 31 | } 32 | } 33 | 34 | return div; 35 | } 36 | 37 | export const cleanElement = (target) => document.getElementById(target).innerHTML = ''; -------------------------------------------------------------------------------- /12 Promises/00_start/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /12 Promises/00_start/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | API weather 10 | 11 | 12 | 13 |

API Rest

14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /12 Promises/00_start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "promises_demo", 3 | "version": "1.0.0", 4 | "description": "Promises ES6", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Promises", 12 | "ES6" 13 | ], 14 | "author": "Jaime Salas", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@babel/core": "^7.8.4", 18 | "@babel/plugin-proposal-class-properties": "^7.8.3", 19 | "parcel": "^1.12.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12 Promises/00_start/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /12 Promises/00_start/src/js/Mapper.js: -------------------------------------------------------------------------------- 1 | export class Mapper { 2 | currentWeather(data) { 3 | return data.weather.map((element) => ({ 4 | description: element.description, 5 | icon: element.icon, 6 | main: element.main 7 | })); 8 | } 9 | 10 | forecastWeather(data) { 11 | return data.list.map((element) => ({ 12 | date: element.dt_txt.split(' ')[0], 13 | time: element.dt_txt.split(' ')[1], 14 | weather: element.weather 15 | })); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /12 Promises/00_start/src/js/main.js: -------------------------------------------------------------------------------- 1 | import { Drawer } from './Drawer'; 2 | import { Mapper } from './Mapper'; 3 | import * as weatherAPI from './weatherAPI'; 4 | 5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => { 6 | const data = JSON.parse(resultJSON); 7 | const weatherSummaryEntities = mapper.currentWeather(data); 8 | drawer.drawWeather(weatherSummaryEntities); 9 | }; 10 | 11 | const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => { 12 | const data = JSON.parse(resultJSON); 13 | const forecastSummaryEntities = mapper.forecastWeather(data); 14 | drawer.drawForecastResult(forecastSummaryEntities); 15 | } 16 | 17 | const errorHandler = (err) => console.log(err); 18 | 19 | document.addEventListener('DOMContentLoaded', () => { 20 | const button = document.getElementById('button-weather'); 21 | const drawer = new Drawer('results'); 22 | const mapper = new Mapper(); 23 | 24 | button.addEventListener('click', (event) => { 25 | event.stopPropagation(); 26 | const cityInput = document.getElementById('input-city'); 27 | if (cityInput.value) { 28 | weatherAPI.getCurrentWeather({ name: cityInput.value }) 29 | .then(handlerCurrentWeatherSuccess(mapper, drawer)) 30 | .catch(errorHandler); 31 | 32 | weatherAPI.getForecastWeather({ name: cityInput.value }) 33 | .then(handlerForecastWeatherSuccess(mapper, drawer)) 34 | .catch(errorHandler); 35 | } 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /12 Promises/00_start/src/js/weatherAPI.js: -------------------------------------------------------------------------------- 1 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6'; 2 | 3 | export const getCurrentWeather = (city) => { 4 | let promise = new Promise((resolve, reject) => { 5 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`; 6 | const client = new XMLHttpRequest(); 7 | client.onload = (event) => resolve(event.target.responseText);; 8 | client.onerror = (event) => reject(event.target.statusText); 9 | client.open('get', uri); // By default async 10 | client.send(); 11 | }); 12 | 13 | return promise; 14 | }; 15 | 16 | export const getForecastWeather = (city) => { 17 | let promise = new Promise((resolve, reject) => { 18 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`; 19 | const client = new XMLHttpRequest(); 20 | client.onload = (event) => resolve(event.target.responseText); 21 | client.onerror = (event) => reject(event.target.statusText); 22 | client.open('get', uri); // By default async 23 | client.send(); 24 | }); 25 | 26 | return promise; 27 | }; -------------------------------------------------------------------------------- /12 Promises/01_promise_chaining/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /12 Promises/01_promise_chaining/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | API weather 10 | 11 | 12 | 13 |

API Rest

14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /12 Promises/01_promise_chaining/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "promises_demo", 3 | "version": "1.0.0", 4 | "description": "Promises ES6", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Promises", 12 | "ES6" 13 | ], 14 | "author": "Jaime Salas", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@babel/core": "^7.8.4", 18 | "@babel/plugin-proposal-class-properties": "^7.8.3", 19 | "parcel": "^1.12.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12 Promises/01_promise_chaining/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /12 Promises/01_promise_chaining/src/js/Mapper.js: -------------------------------------------------------------------------------- 1 | export class Mapper { 2 | currentWeather(data) { 3 | return data.weather.map((element) => ({ 4 | description: element.description, 5 | icon: element.icon, 6 | main: element.main 7 | })); 8 | } 9 | 10 | forecastWeather(data) { 11 | return data.list.map((element) => ({ 12 | date: element.dt_txt.split(' ')[0], 13 | time: element.dt_txt.split(' ')[1], 14 | weather: element.weather 15 | })); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /12 Promises/01_promise_chaining/src/js/main.js: -------------------------------------------------------------------------------- 1 | import { Drawer } from './Drawer'; 2 | import { Mapper } from './Mapper'; 3 | import * as weatherAPI from './weatherAPI'; 4 | 5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => { 6 | const data = JSON.parse(resultJSON); 7 | const weatherSummaryEntities = mapper.currentWeather(data); 8 | drawer.drawWeather(weatherSummaryEntities); 9 | }; 10 | 11 | const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => { 12 | const data = JSON.parse(resultJSON); 13 | const forecastSummaryEntities = mapper.forecastWeather(data); 14 | drawer.drawForecastResult(forecastSummaryEntities); 15 | } 16 | 17 | const forecastWeatherRequest = (city) => ( 18 | weatherAPI.getForecastWeather(city) 19 | ); 20 | 21 | const errorHandler = (err) => console.log(err); 22 | 23 | document.addEventListener('DOMContentLoaded', () => { 24 | const button = document.getElementById('button-weather'); 25 | const drawer = new Drawer('results'); 26 | const mapper = new Mapper(); 27 | 28 | button.addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | const cityInput = document.getElementById('input-city'); 31 | if (cityInput.value) { 32 | weatherAPI.getCurrentWeather({ name: cityInput.value }) 33 | .then((resultJSON) => { 34 | handlerCurrentWeatherSuccess(mapper, drawer)(resultJSON); 35 | return forecastWeatherRequest({ name: cityInput.value }); 36 | }) 37 | .then(handlerForecastWeatherSuccess(mapper, drawer)) 38 | .catch(errorHandler); 39 | } 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /12 Promises/01_promise_chaining/src/js/weatherAPI.js: -------------------------------------------------------------------------------- 1 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6'; 2 | 3 | export const getCurrentWeather = (city) => { 4 | let promise = new Promise((resolve, reject) => { 5 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`; 6 | const client = new XMLHttpRequest(); 7 | client.onload = (event) => resolve(event.target.responseText);; 8 | client.onerror = (event) => reject(event.target.statusText); 9 | client.open('get', uri); // By default async 10 | client.send(); 11 | }); 12 | 13 | return promise; 14 | }; 15 | 16 | export const getForecastWeather = (city) => { 17 | let promise = new Promise((resolve, reject) => { 18 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`; 19 | const client = new XMLHttpRequest(); 20 | client.onload = (event) => resolve(event.target.responseText); 21 | client.onerror = (event) => reject(event.target.statusText); 22 | client.open('get', uri); // By default async 23 | client.send(); 24 | }); 25 | 26 | return promise; 27 | }; -------------------------------------------------------------------------------- /12 Promises/02_promise_all/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /12 Promises/02_promise_all/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | API weather 10 | 11 | 12 | 13 |

API Rest

14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /12 Promises/02_promise_all/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "promises_demo", 3 | "version": "1.0.0", 4 | "description": "Promises ES6", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Promises", 12 | "ES6" 13 | ], 14 | "author": "Jaime Salas", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@babel/core": "^7.8.4", 18 | "@babel/plugin-proposal-class-properties": "^7.8.3", 19 | "parcel": "^1.12.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12 Promises/02_promise_all/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /12 Promises/02_promise_all/src/js/Mapper.js: -------------------------------------------------------------------------------- 1 | export class Mapper { 2 | currentWeather(data) { 3 | return data.weather.map((element) => ({ 4 | description: element.description, 5 | icon: element.icon, 6 | main: element.main 7 | })); 8 | } 9 | 10 | forecastWeather(data) { 11 | return data.list.map((element) => ({ 12 | date: element.dt_txt.split(' ')[0], 13 | time: element.dt_txt.split(' ')[1], 14 | weather: element.weather 15 | })); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /12 Promises/02_promise_all/src/js/main.js: -------------------------------------------------------------------------------- 1 | import { Drawer } from './Drawer'; 2 | import { Mapper } from './Mapper'; 3 | import * as weatherAPI from './weatherAPI'; 4 | 5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => { 6 | const data = JSON.parse(resultJSON); 7 | const weatherSummaryEntities = mapper.currentWeather(data); 8 | drawer.drawWeather(weatherSummaryEntities); 9 | }; 10 | 11 | const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => { 12 | const data = JSON.parse(resultJSON); 13 | const forecastSummaryEntities = mapper.forecastWeather(data); 14 | drawer.drawForecastResult(forecastSummaryEntities); 15 | } 16 | 17 | const forecastWeatherRequest = (city) => ( 18 | weatherAPI.getForecastWeather(city) 19 | ); 20 | 21 | const errorHandler = (err) => console.log(err); 22 | 23 | document.addEventListener('DOMContentLoaded', () => { 24 | const button = document.getElementById('button-weather'); 25 | const drawer = new Drawer('results'); 26 | const mapper = new Mapper(); 27 | 28 | button.addEventListener('click', (event) => { 29 | event.stopPropagation(); 30 | const cityInput = document.getElementById('input-city'); 31 | if (cityInput.value) { 32 | Promise.all([ 33 | weatherAPI.getCurrentWeather({ name: cityInput.value }), 34 | weatherAPI.getForecastWeather({ name: cityInput.value }) 35 | ]) 36 | .catch(errorHandler) 37 | .then((results) => { 38 | handlerCurrentWeatherSuccess(mapper, drawer)(results[0]); 39 | handlerForecastWeatherSuccess(mapper, drawer)(results[1]); 40 | }); 41 | } 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /12 Promises/02_promise_all/src/js/weatherAPI.js: -------------------------------------------------------------------------------- 1 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6'; 2 | 3 | export const getCurrentWeather = (city) => { 4 | let promise = new Promise((resolve, reject) => { 5 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`; 6 | const client = new XMLHttpRequest(); 7 | client.onload = (event) => resolve(event.target.responseText);; 8 | client.onerror = (event) => reject(event.target.statusText); 9 | client.open('get', uri); // By default async 10 | client.send(); 11 | }); 12 | 13 | return promise; 14 | }; 15 | 16 | export const getForecastWeather = (city) => { 17 | let promise = new Promise((resolve, reject) => { 18 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`; 19 | const client = new XMLHttpRequest(); 20 | client.onload = (event) => resolve(event.target.responseText); 21 | client.onerror = (event) => reject(event.target.statusText); 22 | client.open('get', uri); // By default async 23 | client.send(); 24 | }); 25 | 26 | return promise; 27 | }; -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | API weather 10 | 11 | 12 | 13 |

API Rest

14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "promises_demo", 3 | "version": "1.0.0", 4 | "description": "Promises ES6", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Promises", 12 | "ES6" 13 | ], 14 | "author": "Jaime Salas", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@babel/core": "^7.8.4", 18 | "@babel/plugin-proposal-class-properties": "^7.8.3", 19 | "parcel": "^1.12.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/Mapper.js: -------------------------------------------------------------------------------- 1 | export class Mapper { 2 | currentWeather(data) { 3 | return data.weather.map((element) => ({ 4 | description: element.description, 5 | icon: element.icon, 6 | main: element.main 7 | })); 8 | } 9 | 10 | forecastWeather(data) { 11 | return data.list.map((element) => ({ 12 | date: element.dt_txt.split(' ')[0], 13 | time: element.dt_txt.split(' ')[1], 14 | weather: element.weather 15 | })); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/httpClient.js: -------------------------------------------------------------------------------- 1 | const ajax = (method, url, args) => ( 2 | new Promise((resolve, reject) => { 3 | const client = new XMLHttpRequest(); 4 | let uri = url; 5 | let params; 6 | // TRIBUTE: https://plainjs.com/javascript/ajax/send-ajax-get-and-post-requests-47/ 7 | if (method === 'POST') { 8 | params = typeof args == 'string' ? args : Object.keys(args).map( 9 | (k) => (encodeURIComponent(k) + '=' + encodeURIComponent(args[k])) 10 | ).join('&'); 11 | } 12 | client.open(method, uri); 13 | if (method === 'POST') { 14 | client.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 15 | client.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 16 | } 17 | client.onload = (event) => { 18 | resolve(event.target.responseText); 19 | }; 20 | client.onerror = (event) => { 21 | reject(event.target.statusText); 22 | }; 23 | if (method === 'POST') { 24 | client.send(params); 25 | } else { 26 | client.send(); 27 | } 28 | }) 29 | ); 30 | 31 | export const httpClient = { 32 | 'get': (url, args) => ajax('GET', url, args), 33 | 'post': (url, args) => ajax('POST', url, args), 34 | }; -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/main.js: -------------------------------------------------------------------------------- 1 | import { Drawer } from './Drawer'; 2 | import { Mapper } from './Mapper'; 3 | import * as weatherAPI from './weatherAPI'; 4 | 5 | // const handlerCurrentWeatherSuccess = (mapper, drawer) => (resultJSON) => { 6 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (data) => { 7 | // const data = JSON.parse(resultJSON); 8 | const weatherSummaryEntities = mapper.currentWeather(data); 9 | drawer.drawWeather(weatherSummaryEntities); 10 | }; 11 | 12 | // const handlerForecastWeatherSuccess = (mapper, drawer) => (resultJSON) => { 13 | const handlerForecastWeatherSuccess = (mapper, drawer) => (data) => { 14 | // const data = JSON.parse(resultJSON); 15 | const forecastSummaryEntities = mapper.forecastWeather(data); 16 | drawer.drawForecastResult(forecastSummaryEntities); 17 | } 18 | 19 | const forecastWeatherRequest = (city) => ( 20 | weatherAPI.getForecastWeather(city) 21 | ); 22 | 23 | const errorHandler = (err) => console.log(err); 24 | 25 | document.addEventListener('DOMContentLoaded', () => { 26 | const button = document.getElementById('button-weather'); 27 | const drawer = new Drawer('results'); 28 | const mapper = new Mapper(); 29 | 30 | button.addEventListener('click', (event) => { 31 | event.stopPropagation(); 32 | const cityInput = document.getElementById('input-city'); 33 | if (cityInput.value) { 34 | Promise.all([ 35 | weatherAPI.getCurrentWeather({ name: cityInput.value }), 36 | weatherAPI.getForecastWeather({ name: cityInput.value }) 37 | ]) 38 | .catch(errorHandler) 39 | .then((results) => { 40 | handlerCurrentWeatherSuccess(mapper, drawer)(results[0]); 41 | handlerForecastWeatherSuccess(mapper, drawer)(results[1]); 42 | }); 43 | } 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /12 Promises/03_avoiding_boilerplate_XMLHttpRequest/src/js/weatherAPI.js: -------------------------------------------------------------------------------- 1 | import { httpClient } from './httpClient'; 2 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6'; 3 | 4 | export const getCurrentWeather = (city) => { 5 | // let promise = new Promise((resolve, reject) => { 6 | // const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`; 7 | // const client = new XMLHttpRequest(); 8 | // client.onload = (event) => resolve(event.target.responseText);; 9 | // client.onerror = (event) => reject(event.target.statusText); 10 | // client.open('get', uri); // By default async 11 | // client.send(); 12 | // }); 13 | 14 | // return promise; 15 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`; 16 | return httpClient.get(uri); 17 | }; 18 | 19 | export const getForecastWeather = (city) => { 20 | // let promise = new Promise((resolve, reject) => { 21 | // const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`; 22 | // const client = new XMLHttpRequest(); 23 | // client.onload = (event) => resolve(event.target.responseText); 24 | // client.onerror = (event) => reject(event.target.statusText); 25 | // client.open('get', uri); // By default async 26 | // client.send(); 27 | // }); 28 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`; 29 | return httpClient.get(uri); 30 | }; -------------------------------------------------------------------------------- /12 Promises/04_promise_race/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-proposal-class-properties" 4 | ] 5 | } -------------------------------------------------------------------------------- /12 Promises/04_promise_race/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | API weather 10 | 11 | 12 | 13 |

API Rest

14 | 15 | 16 |
17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /12 Promises/04_promise_race/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "promises_demo", 3 | "version": "1.0.0", 4 | "description": "Promises ES6", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Promises", 12 | "ES6" 13 | ], 14 | "author": "Jaime Salas", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "@babel/core": "^7.8.4", 18 | "@babel/plugin-proposal-class-properties": "^7.8.3", 19 | "parcel": "^1.12.4" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /12 Promises/04_promise_race/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | 11 | .container p { 12 | max-width: 70%; 13 | font-size: 0.9em; 14 | margin-top: 0.2em; 15 | margin-bottom: 0.2em; 16 | height: 2.3em; 17 | } 18 | 19 | .container p img { 20 | max-width: 95%; 21 | max-height: 95%; 22 | position: relative; 23 | top: 0.7em; 24 | left: 1em; 25 | } 26 | 27 | .container ul { 28 | list-style: none; 29 | } 30 | -------------------------------------------------------------------------------- /12 Promises/04_promise_race/src/js/Mapper.js: -------------------------------------------------------------------------------- 1 | export class Mapper { 2 | currentWeather(data) { 3 | return data.weather.map((element) => ({ 4 | description: element.description, 5 | icon: element.icon, 6 | main: element.main 7 | })); 8 | } 9 | 10 | forecastWeather(data) { 11 | return data.list.map((element) => ({ 12 | date: element.dt_txt.split(' ')[0], 13 | time: element.dt_txt.split(' ')[1], 14 | weather: element.weather 15 | })); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /12 Promises/04_promise_race/src/js/httpClient.js: -------------------------------------------------------------------------------- 1 | const ajax = (method, url, args) => ( 2 | new Promise((resolve, reject) => { 3 | const client = new XMLHttpRequest(); 4 | let uri = url; 5 | let params; 6 | // TRIBUTE: https://plainjs.com/javascript/ajax/send-ajax-get-and-post-requests-47/ 7 | if (method === 'POST') { 8 | params = typeof args == 'string' ? args : Object.keys(args).map( 9 | (k) => (encodeURIComponent(k) + '=' + encodeURIComponent(args[k])) 10 | ).join('&'); 11 | } 12 | client.open(method, uri); 13 | if (method === 'POST') { 14 | client.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 15 | client.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 16 | } 17 | client.onload = (event) => { 18 | // TODO: Check status code. 19 | const result = JSON.parse(event.target.response); 20 | resolve(result); 21 | }; 22 | client.onerror = (event) => { 23 | // TODO: Check status code. 24 | const result = JSON.parse(event.target.responseText); 25 | reject(result); 26 | }; 27 | if (method === 'POST') { 28 | client.send(params); 29 | } else { 30 | client.send(); 31 | } 32 | }) 33 | ); 34 | 35 | export const httpClient = { 36 | 'get': (url, args) => ajax('GET', url, args), 37 | 'post': (url, args) => ajax('POST', url, args), 38 | }; -------------------------------------------------------------------------------- /12 Promises/04_promise_race/src/js/main.js: -------------------------------------------------------------------------------- 1 | import { Drawer } from './Drawer'; 2 | import { Mapper } from './Mapper'; 3 | import * as weatherAPI from './weatherAPI'; 4 | 5 | const handlerCurrentWeatherSuccess = (mapper, drawer) => (data) => { 6 | const weatherSummaryEntities = mapper.currentWeather(data); 7 | drawer.drawWeather(weatherSummaryEntities); 8 | }; 9 | 10 | const handlerForecastWeatherSuccess = (mapper, drawer) => (data) => { 11 | const forecastSummaryEntities = mapper.forecastWeather(data); 12 | drawer.drawForecastResult(forecastSummaryEntities); 13 | } 14 | 15 | const forecastWeatherRequest = (city) => ( 16 | weatherAPI.getForecastWeather(city) 17 | ); 18 | 19 | const errorHandler = (err) => console.log(err); 20 | 21 | document.addEventListener('DOMContentLoaded', () => { 22 | const button = document.getElementById('button-weather'); 23 | const drawer = new Drawer('results'); 24 | const mapper = new Mapper(); 25 | 26 | button.addEventListener('click', (event) => { 27 | event.stopPropagation(); 28 | const cityInput = document.getElementById('input-city'); 29 | if (cityInput.value) { 30 | Promise.race([ 31 | weatherAPI.getCurrentWeather({ name: cityInput.value }), 32 | weatherAPI.getForecastWeather({ name: cityInput.value }) 33 | ]) 34 | .catch(errorHandler) 35 | .then((data) => { 36 | if (data.hasOwnProperty('weather')) { 37 | handlerCurrentWeatherSuccess(mapper, drawer)(data); 38 | } else { 39 | handlerForecastWeatherSuccess(mapper, drawer)(data); 40 | } 41 | }); 42 | } 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /12 Promises/04_promise_race/src/js/weatherAPI.js: -------------------------------------------------------------------------------- 1 | import { httpClient } from './httpClient'; 2 | const apiKey = '855fb3867fa7108f3d6a43d7405878e6'; 3 | 4 | export const getCurrentWeather = (city) => { 5 | // let promise = new Promise((resolve, reject) => { 6 | // const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`; 7 | // const client = new XMLHttpRequest(); 8 | // client.onload = (event) => resolve(event.target.responseText);; 9 | // client.onerror = (event) => reject(event.target.statusText); 10 | // client.open('get', uri); // By default async 11 | // client.send(); 12 | // }); 13 | 14 | // return promise; 15 | const uri = `http://api.openweathermap.org/data/2.5/weather?q=${city.name}&appid=${apiKey}`; 16 | return httpClient.get(uri); 17 | }; 18 | 19 | export const getForecastWeather = (city) => { 20 | // let promise = new Promise((resolve, reject) => { 21 | // const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`; 22 | // const client = new XMLHttpRequest(); 23 | // client.onload = (event) => resolve(event.target.responseText); 24 | // client.onerror = (event) => reject(event.target.statusText); 25 | // client.open('get', uri); // By default async 26 | // client.send(); 27 | // }); 28 | const uri = `http://api.openweathermap.org/data/2.5/forecast?q=${city.name}&appid=${apiKey}`; 29 | return httpClient.get(uri); 30 | }; -------------------------------------------------------------------------------- /13 Fetch/00_start/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Async fetch 7 | 8 | 9 | 10 | 11 |

Fetch

12 |
13 | 14 |
15 | Title:
16 | 17 |
18 | 19 | Genre:
20 | 21 |
22 | 23 | Author:
24 | 25 |
26 | 27 | Have you read this book?:
28 | 29 |

30 | 31 |
32 |
33 |
34 | 35 |
36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /13 Fetch/00_start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fetch-fundamentals", 3 | "version": "0.0.0", 4 | "description": "A repository to learn the fetch basics", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "parcel index.html", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "fetch", 12 | "async", 13 | "javascript" 14 | ], 15 | "author": "Jaime Salas", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "parcel": "^1.12.3" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /13 Fetch/00_start/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } -------------------------------------------------------------------------------- /13 Fetch/00_start/src/js/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | 7 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 8 | 9 | list.appendChild(listItem); 10 | }); 11 | 12 | return list; 13 | }; 14 | 15 | export const appendElement = (target, item) => { 16 | document.getElementById(target).appendChild(item); 17 | }; -------------------------------------------------------------------------------- /13 Fetch/01_createRequestFactory/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Async fetch 7 | 8 | 9 | 10 | 11 |

Fetch

12 |
13 | 14 |
15 | Title:
16 | 17 |
18 | 19 | Genre:
20 | 21 |
22 | 23 | Author:
24 | 25 |
26 | 27 | Have you read this book?:
28 | 29 |

30 | 31 |
32 |
33 |
34 | 35 |
36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /13 Fetch/01_createRequestFactory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_createrequestfactory", 3 | "version": "1.0.0", 4 | "description": "Playing with fetch demo", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Fetch" 12 | ], 13 | "author": "Jaime Salas", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "parcel": "^1.8.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /13 Fetch/01_createRequestFactory/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /13 Fetch/01_createRequestFactory/src/js/bookAPI.js: -------------------------------------------------------------------------------- 1 | const createRequest = (url, options) => { 2 | const _options = (options) ? options : {}; 3 | console.log(_options); 4 | return fetch(url, _options) 5 | .then((response) => { 6 | if (!response.ok) { 7 | throw Error(response.statusText); 8 | } 9 | return response.json(); 10 | }); 11 | }; 12 | 13 | const baseURL = 'http://localhost:8000/api/books'; 14 | 15 | export const retrieveBooks = () => { 16 | return createRequest(baseURL, { 17 | method: 'GET', 18 | mode: 'cors', 19 | headers: { 20 | 'Accept': 'application/json', 21 | 'Content-Type': 'application/json' 22 | }, 23 | }); 24 | }; 25 | 26 | export const retrieveBook = (bookId) => { 27 | const url = `${baseURL}/${bookId}`; 28 | return createRequest(url, { 29 | method: 'GET', 30 | mode: 'cors', 31 | headers: { 32 | 'Accept': 'application/json', 33 | 'Content-Type': 'application/json' 34 | }, 35 | }); 36 | } -------------------------------------------------------------------------------- /13 Fetch/01_createRequestFactory/src/js/main.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from './bookAPI'; 2 | 3 | document.addEventListener('DOMContentLoaded', () => { 4 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 5 | buttonRetrieveBooks.addEventListener('click', (event) => { 6 | event.stopPropagation(); 7 | bookAPI.retrieveBook('noexists') 8 | .then((result) => console.log(result)) 9 | .catch((err) => console.log(err.message, 'main')); 10 | // bookAPI.retrieveBooks() 11 | // .then((result) => console.log(result)) 12 | // .catch((err) => console.log(err.message, 'main')); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /13 Fetch/01_createRequestFactory/src/js/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | 7 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 8 | 9 | list.appendChild(listItem); 10 | }); 11 | 12 | return list; 13 | }; 14 | 15 | export const appendElement = (target, item) => { 16 | document.getElementById(target).appendChild(item); 17 | }; 18 | -------------------------------------------------------------------------------- /13 Fetch/01_requestFactory/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LEMONCODE 16/17 Async JavaScript jQuery 7 | 8 | 9 | 10 | 11 |

AJAX jQuery

12 |
13 | 14 |
15 | Title:
16 | 17 |
18 | 19 | Genre:
20 | 21 |
22 | 23 | Author:
24 | 25 |
26 | 27 | Have you read this book?:
28 | 29 |

30 | 31 |
32 |
33 |
34 | 35 |
36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /13 Fetch/01_requestFactory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "00_createrequestfactory", 3 | "version": "1.0.0", 4 | "description": "Playing with fetch demo", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Fetch" 12 | ], 13 | "author": "Jaime Salas", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "parcel": "^1.8.1" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /13 Fetch/01_requestFactory/src/content/site.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Raleway'); 2 | 3 | body { 4 | font-family: 'Raleway', sans-serif; 5 | } 6 | 7 | .container { 8 | padding: 2em; 9 | } 10 | -------------------------------------------------------------------------------- /13 Fetch/01_requestFactory/src/js/bookAPI.js: -------------------------------------------------------------------------------- 1 | const baseURL = 'http://localhost:8000/api/books'; 2 | 3 | const requestOptionsFactory = (headers) => (...args) => { 4 | const [method, mode] = [...args]; 5 | return { 6 | method, 7 | mode, 8 | headers 9 | }; 10 | }; 11 | const requestJson = requestOptionsFactory(new Headers({ 'Content-Type': 'application/json' })); 12 | 13 | const handleErrors = (response) => { 14 | if (!response.ok) { 15 | throw Error(response.statusText); 16 | } 17 | return response; 18 | }; 19 | 20 | const fetchJson = (request) => fetch(request) 21 | .then(handleErrors) 22 | .then((response) => response.json()); 23 | 24 | export const retrieveBooks = () => { 25 | return fetchJson( 26 | new Request(baseURL, requestJson('GET', 'cors')) 27 | ); 28 | }; 29 | 30 | export const retrieveBook = (bookId) => { 31 | const url = `${baseURL}/${bookId}`; 32 | return fetchJson( 33 | new Request(url, requestJson('GET', 'cors')) 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /13 Fetch/01_requestFactory/src/js/main.js: -------------------------------------------------------------------------------- 1 | import * as bookAPI from './bookAPI'; 2 | 3 | document.addEventListener('DOMContentLoaded', () => { 4 | const buttonRetrieveBooks = document.getElementById('button-retrieve-books'); 5 | buttonRetrieveBooks.addEventListener('click', (event) => { 6 | event.stopPropagation(); 7 | bookAPI.retrieveBook('noexists') 8 | .then((result) => console.log(result)) 9 | .catch((err) => console.log(err.message)); 10 | }); 11 | }); 12 | -------------------------------------------------------------------------------- /13 Fetch/01_requestFactory/src/js/uiBuilder.js: -------------------------------------------------------------------------------- 1 | export const createList = (elements) => { 2 | let list = document.createElement('ul'); 3 | 4 | elements.forEach((element) => { 5 | let listItem = document.createElement('li'); 6 | 7 | listItem.appendChild(document.createTextNode(element)); // Use mapper here, to extract and format data. 8 | 9 | list.appendChild(listItem); 10 | }); 11 | 12 | return list; 13 | }; 14 | 15 | export const appendElement = (target, item) => { 16 | document.getElementById(target).appendChild(item); 17 | }; 18 | -------------------------------------------------------------------------------- /14 Async await/00_start/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-transform-runtime" 4 | ] 5 | } -------------------------------------------------------------------------------- /14 Async await/00_start/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /14 Async await/00_start/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async_await_demos", 3 | "version": "1.0.0", 4 | "description": "Async await tutorial", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Async", 12 | "await", 13 | "native" 14 | ], 15 | "author": "Jaime Salas", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@babel/core": "^7.8.4", 19 | "@babel/plugin-transform-runtime": "^7.8.3", 20 | "@babel/runtime": "^7.8.4", 21 | "parcel": "^1.12.4" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /14 Async await/00_start/src/api/BookApiClient.js: -------------------------------------------------------------------------------- 1 | export class BookApiClient { 2 | async fetchBooks() { 3 | const url = 'http://localhost:8000/api/books/'; 4 | const response = await fetch(url); 5 | return await response.json(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /14 Async await/00_start/src/api/bookAPI.js: -------------------------------------------------------------------------------- 1 | export async function showBooks() { 2 | const url = 'http://localhost:8000/api/books/'; 3 | const response = await fetch(url); 4 | // const books = await response.json(); 5 | // return books; 6 | return await response.json(); 7 | } 8 | 9 | export const functionExpressionShowBooks = async () => { 10 | const url = 'http://localhost:8000/api/books/'; 11 | const response = await fetch(url); 12 | return await response.json(); 13 | } 14 | -------------------------------------------------------------------------------- /14 Async await/00_start/src/app.js: -------------------------------------------------------------------------------- 1 | import "regenerator-runtime/runtime"; 2 | // async function showBooks() { 3 | // const url = 'http://localhost:8000/api/books/'; 4 | // const response = await fetch(url); 5 | // const books = await response.json(); 6 | // console.log(books); 7 | // } 8 | 9 | // showBooks(); 10 | import { showBooks, functionExpressionShowBooks } from './api/bookAPI'; 11 | import { BookApiClient } from './api/BookApiClient'; 12 | // async function main() { 13 | // const books = await showBooks(); 14 | // console.log(books); 15 | // } 16 | 17 | // main(); 18 | // functionExpressionShowBooks() 19 | // .then((bs) => console.log(bs)); 20 | (async() => { 21 | const bookAPI = new BookApiClient(); 22 | const books = await bookAPI.fetchBooks(); 23 | console.log(books); 24 | })(); 25 | -------------------------------------------------------------------------------- /14 Async await/01_errors/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-transform-runtime" 4 | ] 5 | } -------------------------------------------------------------------------------- /14 Async await/01_errors/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /14 Async await/01_errors/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async_await_demos", 3 | "version": "1.0.0", 4 | "description": "Async await tutorial", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Async", 12 | "await", 13 | "native" 14 | ], 15 | "author": "Jaime Salas", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@babel/core": "^7.8.4", 19 | "@babel/plugin-transform-runtime": "^7.8.3", 20 | "parcel": "^1.12.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /14 Async await/01_errors/src/api/BookApiClient.js: -------------------------------------------------------------------------------- 1 | export class BookApiClient { 2 | async fetchBooks() { 3 | const url = 'http://localhost:8000/api/books/'; 4 | const response = await fetch(url); 5 | return await response.json(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /14 Async await/01_errors/src/api/bookAPI.js: -------------------------------------------------------------------------------- 1 | export async function showBooks(url) { 2 | const response = await fetch(url); 3 | if (!response.ok) { 4 | const err = await response.text(); 5 | throw Error(err); 6 | } 7 | const body = await response.json(); 8 | return body; 9 | } 10 | 11 | export const functionExpressionShowBooks = async () => { 12 | const url = 'http://localhost:8000/api/books/'; 13 | const response = await fetch(url); 14 | return await response.json(); 15 | } 16 | -------------------------------------------------------------------------------- /14 Async await/01_errors/src/app.js: -------------------------------------------------------------------------------- 1 | import { showBooks, functionExpressionShowBooks } from './api/bookAPI'; 2 | import { BookApiClient } from './api/BookApiClient'; 3 | 4 | const url = 'http://localhost:8000/api/boos'; 5 | const url2 = 'http://localhost:8000/api/books/5'; 6 | 7 | // showBooks(url) 8 | // .then((books) => console.log(books)) 9 | // .catch((err) => console.log(err)); 10 | (async () => { 11 | try { 12 | const response = await showBooks(url2); 13 | } catch (error) { 14 | console.log(error); 15 | } 16 | })(); 17 | -------------------------------------------------------------------------------- /14 Async await/02_parallel/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-transform-runtime" 4 | ] 5 | } -------------------------------------------------------------------------------- /14 Async await/02_parallel/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /14 Async await/02_parallel/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async_await_demos", 3 | "version": "1.0.0", 4 | "description": "Async await tutorial", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Async", 12 | "await", 13 | "native" 14 | ], 15 | "author": "Jaime Salas", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@babel/core": "^7.8.4", 19 | "@babel/plugin-transform-runtime": "^7.8.3", 20 | "parcel": "^1.12.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /14 Async await/02_parallel/src/api/BookApiClient.js: -------------------------------------------------------------------------------- 1 | export class BookApiClient { 2 | async fetchBooks() { 3 | const url = 'http://localhost:8000/api/books/'; 4 | const response = await fetch(url); 5 | return await response.json(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /14 Async await/02_parallel/src/api/bookAPI.js: -------------------------------------------------------------------------------- 1 | export async function showBooks(url) { 2 | const response = await fetch(url); 3 | if (!response.ok) { 4 | const err = await response.text(); 5 | throw Error(err); 6 | } 7 | const body = await response.json(); 8 | return body; 9 | } 10 | 11 | export async function fetchFromAPI(endpoint) { 12 | const url = `http://localhost:8000/api${endpoint}`; 13 | const response = await fetch(url); 14 | return await response.json(); 15 | } 16 | 17 | export const functionExpressionShowBooks = async () => { 18 | const url = 'http://localhost:8000/api/books/'; 19 | const response = await fetch(url); 20 | return await response.json(); 21 | } 22 | -------------------------------------------------------------------------------- /14 Async await/02_parallel/src/app.js: -------------------------------------------------------------------------------- 1 | import { showBooks, functionExpressionShowBooks, fetchFromAPI } from './api/bookAPI'; 2 | import { BookApiClient } from './api/BookApiClient'; 3 | 4 | const show = async () => { 5 | const [books, authors] = await Promise.all([ 6 | fetchFromAPI('/books/'), 7 | fetchFromAPI('/authors/'), 8 | ]); 9 | 10 | console.log(`Books: ${books.length}, Authors: ${authors.length}`); 11 | }; 12 | 13 | show(); -------------------------------------------------------------------------------- /14 Async await/03_sequential/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "@babel/plugin-transform-runtime" 4 | ] 5 | } -------------------------------------------------------------------------------- /14 Async await/03_sequential/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /14 Async await/03_sequential/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "async_await_demos", 3 | "version": "1.0.0", 4 | "description": "Async await tutorial", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "parcel index.html" 9 | }, 10 | "keywords": [ 11 | "Async", 12 | "await", 13 | "native" 14 | ], 15 | "author": "Jaime Salas", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "@babel/core": "^7.8.4", 19 | "@babel/plugin-transform-runtime": "^7.8.3", 20 | "parcel": "^1.12.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /14 Async await/03_sequential/src/api/BookApiClient.js: -------------------------------------------------------------------------------- 1 | export class BookApiClient { 2 | async fetchBooks() { 3 | const url = 'http://localhost:8000/api/books/'; 4 | const response = await fetch(url); 5 | return await response.json(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /14 Async await/03_sequential/src/api/bookAPI.js: -------------------------------------------------------------------------------- 1 | export async function showBooks(url) { 2 | const response = await fetch(url); 3 | if (!response.ok) { 4 | const err = await response.text(); 5 | throw Error(err); 6 | } 7 | const body = await response.json(); 8 | return body; 9 | } 10 | 11 | export async function fetchFromAPI(endpoint) { 12 | const url = `http://localhost:8000/api${endpoint}`; 13 | const response = await fetch(url); 14 | return await response.json(); 15 | } 16 | 17 | export const functionExpressionShowBooks = async () => { 18 | const url = 'http://localhost:8000/api/books/'; 19 | const response = await fetch(url); 20 | return await response.json(); 21 | } 22 | -------------------------------------------------------------------------------- /14 Async await/03_sequential/src/app.js: -------------------------------------------------------------------------------- 1 | import { showBooks, functionExpressionShowBooks, fetchFromAPI } from './api/bookAPI'; 2 | import { BookApiClient } from './api/BookApiClient'; 3 | 4 | const show = async () => { 5 | const [books, authors] = await Promise.all([ 6 | fetchFromAPI('/books/'), 7 | fetchFromAPI('/authors/'), 8 | ]); 9 | 10 | console.log(`Books: ${books.length}, Authors: ${authors.length}`); 11 | }; 12 | 13 | show(); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jaime Salas 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # async-javascript-fundamentals 2 | 3 | Getting started with the concurrency model on JavaScript 4 | 5 | # About Basefactor + Lemoncode 6 | 7 | We are an innovating team of Javascript experts, passionate about turning your ideas into robust products. 8 | 9 | [Basefactor, consultancy by Lemoncode](http://www.basefactor.com) provides consultancy and coaching services. 10 | 11 | [Lemoncode](http://lemoncode.net/services/en/#en-home) provides training services. 12 | 13 | For the LATAM/Spanish audience we are running an Online Front End Master degree, more info: http://lemoncode.net/master-frontend -------------------------------------------------------------------------------- /auth/credentialResolver.js: -------------------------------------------------------------------------------- 1 | const jwt = require('jsonwebtoken'); 2 | 3 | const users = [ 4 | { id: 1, username: 'admin', password: 'admin' }, 5 | { id: 2, username: 'guest', password: 'guest' }, 6 | ]; 7 | 8 | exports.resolveLogin = (username, password) => { 9 | let status; 10 | let response; 11 | let emptyCredentials = !username || !password; 12 | if (emptyCredentials) { 13 | status = 400; 14 | response = 'You need a username and password'; 15 | } 16 | 17 | if (!emptyCredentials) { 18 | const user = users.find((u) => 19 | u.username === username && u.password === password 20 | ); 21 | 22 | if (!user) { 23 | status = 401; 24 | response = 'User not found'; 25 | } else { 26 | const token = jwt.sign( 27 | { 28 | sub: user.id, 29 | username: user.username 30 | }, 31 | 'mysupersecretkey', 32 | { expiresIn: '3 hours' } 33 | ); 34 | 35 | status = 200; 36 | response = { access_token: token }; 37 | } 38 | } 39 | 40 | return { 41 | status, 42 | response, 43 | } 44 | }; -------------------------------------------------------------------------------- /auth/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const bodyParser = require('body-parser'); 3 | const cors = require('cors'); 4 | const { resolveLogin } = require('./credentialResolver'); 5 | 6 | const app = express(); 7 | const PORT = process.env.PORT || 8887; 8 | 9 | app.use(bodyParser.json()); 10 | app.use(cors()); 11 | 12 | app.post('/login', (req, res) => { 13 | const { username, password } = req.body; 14 | const { status, response } = resolveLogin(username, password); 15 | res.status(status) 16 | .send(response); 17 | }); 18 | 19 | app.get('*', (_, res) => { 20 | res.sendStatus(404); 21 | }); 22 | 23 | app.listen(PORT, () => { 24 | console.log(`Server is running on ${PORT}`); 25 | }); -------------------------------------------------------------------------------- /auth/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "auth", 3 | "version": "0.0.0", 4 | "description": "auth server for demo purpose", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "JWT", 12 | "nodejs" 13 | ], 14 | "author": "Jaime Salas", 15 | "license": "MIT", 16 | "dependencies": { 17 | "body-parser": "^1.18.3", 18 | "cors": "^2.8.5", 19 | "express": "^4.16.4", 20 | "jsonwebtoken": "^8.5.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.7' 2 | services: 3 | app: 4 | container_name: book-server 5 | environment: 6 | - ENVIRONMENT=mongodb:27017 7 | build: ./server-mongo 8 | ports: 9 | - "8000:8000" 10 | depends_on: 11 | - mongodb 12 | mongodb: 13 | image: mongo:3.2 14 | container_name: 'mongodb' 15 | volumes: 16 | - mongodbdata:/data/db 17 | ports: 18 | - "27017:27017" 19 | volumes: 20 | mongodbdata: -------------------------------------------------------------------------------- /server-in-memory/README.md: -------------------------------------------------------------------------------- 1 | ## This is going to be our protected API. 2 | 3 | * To protect this API, we're going to use _express-jwt_. 4 | * We're sync _auth_ and _API_ by using in both places the same secret. 5 | 6 | ```javascript 7 | const jwtCheck = expressjwt({ 8 | secret: 'mysupersecretkey', 9 | }) 10 | 11 | 12 | app.use('/api/users', jwtCheck, users); 13 | app.use('/api/cars', jwtCheck, cars); 14 | ``` 15 | * If the user is not authenticated will not get response from API -------------------------------------------------------------------------------- /server-in-memory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server_support_service_demos", 3 | "version": "0.0.0", 4 | "description": "API that expose data for client", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node server.js", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "express", 12 | "node", 13 | "rest", 14 | "api" 15 | ], 16 | "author": "Jaime Salas", 17 | "license": "MIT", 18 | "dependencies": { 19 | "body-parser": "^1.18.3", 20 | "cookie-parser": "^1.4.4", 21 | "cors": "^2.8.5", 22 | "express": "^4.16.4", 23 | "express-jwt": "^5.3.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server-in-memory/routes/data/cars.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "car_id": 1, 4 | "name": "ABX", 5 | "brand": "Tokiota", 6 | "year_release": "1999" 7 | }, 8 | { 9 | "car_id": 2, 10 | "name": "AZE", 11 | "brand": "Tokiota", 12 | "year_release": "1995" 13 | }, 14 | { 15 | "car_id": 3, 16 | "name": "Colola", 17 | "brand": "GIA", 18 | "year_release": "2015" 19 | }, 20 | { 21 | "car_id": 57, 22 | "name": "Maverick", 23 | "brand": "Foo", 24 | "year_release": "1965" 25 | }, 26 | { 27 | "car_id": 59, 28 | "name": "Calel", 29 | "brand": "HMI", 30 | "year_release": "1975" 31 | }, 32 | { 33 | "car_id": 61, 34 | "name": "Green Roaster", 35 | "brand": "HMI", 36 | "year_release": "1976" 37 | } 38 | ] -------------------------------------------------------------------------------- /server-in-memory/routes/users.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const router = express.Router(); 3 | 4 | /* GET users listing. */ 5 | router.get('/', (req, res) => { 6 | res.send('respond with a resource'); 7 | }); 8 | 9 | module.exports = router; -------------------------------------------------------------------------------- /server-in-memory/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | path = require('path'), 3 | cookieParser = require('cookie-parser'), 4 | bodyParser = require('body-parser'), 5 | cors = require('cors'), 6 | expressjwt = require('express-jwt'); 7 | 8 | const users = require('./routes/users'); 9 | const cars = require('./routes/cars'); 10 | 11 | const app = express(); 12 | 13 | // setup 14 | app.use(bodyParser.json()); 15 | app.use(bodyParser.urlencoded({ extended: false })); 16 | app.use(cookieParser()); 17 | app.use(cors()); 18 | const jwtCheck = expressjwt({ 19 | secret: 'mysupersecretkey', 20 | }); 21 | 22 | app.use('/api/users', jwtCheck, users); 23 | app.use('/api/cars', jwtCheck, cars); 24 | 25 | 26 | app.set('port', process.env.PORT || 3050); 27 | app.listen(app.get('port')); 28 | 29 | console.log('Listening on port: ' + app.get('port')); 30 | 31 | module.exports = app; 32 | -------------------------------------------------------------------------------- /server-mock/config/routes.json: -------------------------------------------------------------------------------- 1 | { 2 | "/api/*": "/$1" 3 | } -------------------------------------------------------------------------------- /server-mock/mock-data/data.json: -------------------------------------------------------------------------------- 1 | { 2 | "authors": [ 3 | { 4 | "id": 1, 5 | "name": "Laika", 6 | "lastName": "Dog", 7 | "birthDate": "date" 8 | } 9 | ], 10 | "books": [ 11 | { 12 | "id": 1, 13 | "title": "bone", 14 | "author": "Laika", 15 | "genre": "comedy", 16 | "read": true 17 | }, 18 | { 19 | "title": "Lord of flies", 20 | "author": "William Goldwing", 21 | "genre": "Drama", 22 | "read": "on", 23 | "id": 2 24 | } 25 | ] 26 | } -------------------------------------------------------------------------------- /server-mock/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server-mock", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "json-server --routes ./config/routes.json --watch mock-data/data.json --port 8000", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "devDependencies": { 14 | "json-server": "^0.16.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server-mongo/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | Tests/ 3 | -------------------------------------------------------------------------------- /server-mongo/.env: -------------------------------------------------------------------------------- 1 | ENVIRONMENT=localhost -------------------------------------------------------------------------------- /server-mongo/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible Node.js debug attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "node", 9 | "request": "launch", 10 | "name": "Launch Program", 11 | "program": "${workspaceRoot}/app.js" 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /server-mongo/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10.16.0 2 | WORKDIR /usr/src/app 3 | 4 | COPY . . 5 | 6 | RUN npm install 7 | 8 | EXPOSE 8000 9 | CMD ["npm", "start"] -------------------------------------------------------------------------------- /server-mongo/README.md: -------------------------------------------------------------------------------- 1 | ## To start mongo with docker: 2 | 3 | > Reference: https://www.thepolyglotdeveloper.com/2019/01/getting-started-mongodb-docker-container-deployment/ 4 | > Reference: https://dev.to/jay97/docker-compose-an-express-and-mongo-app-aai 5 | > Refereence: https://nodejs.org/de/docs/guides/nodejs-docker-webapp/ 6 | 7 | ```bash 8 | docker-compose up -d 9 | ``` 10 | 11 | ```bash 12 | docker exec -it mongodb bash 13 | ``` 14 | 15 | * This will open a terminal from where we can connect directly to mongo: 16 | 17 | ```bash 18 | mongo 19 | ``` 20 | 21 | 22 | 23 | ## To start mongo with installed insatance windows: 24 | 25 | * mongod --dbpath D:/mongodb/data 26 | 27 | ### Open a new terminal and query the related collections. To start a new terminal we have to use mongo 28 | 29 | ```bash 30 | mongo 31 | ``` 32 | 33 | ### Now that is open, we can inspect the related databases and collections 34 | 35 | ```bash 36 | > show dbs 37 | ``` 38 | 39 | ```bash 40 | > use 41 | ``` 42 | 43 | ```bash 44 | > show 45 | ``` 46 | 47 | ```bash 48 | > db..find() 49 | ``` 50 | 51 | * Remove element by Id from mongo console 52 | ```javascript 53 | db.books.remove({"_id": ObjectId("5d322fcba8a0243b90fd2f95")}); 54 | ``` -------------------------------------------------------------------------------- /server-mongo/Routes/accessRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | getNumber = require('../utils/randomNumber').getNumber; 3 | resolveAccess = require('../controllers/accessControllerBusiness').resolveAccess; 4 | accessRouter = express.Router(); 5 | 6 | const routes = () => { 7 | const accessController = require('../controllers/accessController')(resolveAccess, getNumber); 8 | accessRouter.route('/') 9 | .get(accessController.get); 10 | return accessRouter; 11 | }; 12 | 13 | module.exports = routes; 14 | -------------------------------------------------------------------------------- /server-mongo/Routes/authorRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | authorRouter = express.Router(); 3 | // TODO: Create authorParser 4 | 5 | const routes = function (Author) { // TODO: Inject Author 6 | const authorController = require('../controllers/authorController')(Author); 7 | // Create controller to handle these requests. 8 | authorRouter.route('/') 9 | .post(authorController.post) 10 | .get(authorController.get); 11 | 12 | return authorRouter; 13 | }; 14 | 15 | module.exports = routes; 16 | -------------------------------------------------------------------------------- /server-mongo/Routes/bookRoutes.js: -------------------------------------------------------------------------------- 1 | const express = require('express'), 2 | bookRouter = express.Router(), 3 | bookParser = require('../parsers/bookParser'), 4 | bookIdMiddleware = require('../middlewares/bookMiddleware').bookIdMiddleware; 5 | 6 | const routes = (Book) => { 7 | const bookController = require('../controllers/bookController')(Book, bookParser); 8 | const bookIdController = require('../controllers/bookByIdController')(); 9 | bookRouter.route('/') 10 | .post(bookController.post) 11 | .get(bookController.get); 12 | 13 | bookRouter.use('/:bookId', bookIdMiddleware(Book)); 14 | 15 | bookRouter.route('/:bookId') 16 | .get(bookIdController.get) 17 | .put(bookIdController.put) 18 | .patch(bookIdController.patch) 19 | .delete(bookIdController.delete); // Removes whatever book that it finds on middleware 20 | 21 | return bookRouter; 22 | }; 23 | 24 | module.exports = routes; 25 | -------------------------------------------------------------------------------- /server-mongo/Tests/bookControllerTests.js: -------------------------------------------------------------------------------- 1 | const should = require('should'), 2 | sinon = require('sinon'); 3 | 4 | // We do not need an instance of mocha, because it is currently running on gulp 5 | describe('Book controller tests', function() { 6 | describe('Post', function () { 7 | it('should not allow an empty title on book', function () { 8 | // Arrange 9 | var Book = function (book) { 10 | this.save = function(){} 11 | }; 12 | 13 | var req = { 14 | body: { 15 | author: 'Jhon Doe' 16 | } 17 | }; 18 | 19 | var res = { 20 | status: sinon.spy(), // 21 | send: sinon.spy() // Tracks what is called, what is called with, how many times... 22 | }; 23 | 24 | var bookController = require('../controllers/bookController')(Book); 25 | 26 | // Act 27 | bookController.post(req, res); 28 | 29 | // Assert 30 | res.status.calledWith(400).should.equal(true, 'Bad status ' + res.status.args[0][0]); // Array with the arguments that this function was called 31 | res.send.calledWith('Title is required').should.equal(true); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /server-mongo/Tests/bookIntegrationTests.js: -------------------------------------------------------------------------------- 1 | const should = require('should'), 2 | request = require('supertest'), 3 | app = require('../app.js'), 4 | mongoose = require('mongoose'), 5 | Book = mongoose.model('Book'), // I can pull directly from mongoose because is loaded on app.js 6 | agent = request.agent(app); // Something from supertest to execute all our http calls. 7 | 8 | describe('Book crud Test', function () { // Go through our test database 9 | it('should allow a book to be posted and return a read and _id', function (done) { 10 | const bookPost = { 11 | title: 'new Book', 12 | author: 'Jhon Doe', 13 | genre: 'Fiction' 14 | }; 15 | 16 | agent.post('/api/books') 17 | .send(bookPost) 18 | .expect(200) 19 | .end(function (err, results) { 20 | results.body.read.should.equal(false); 21 | results.body.should.have.property('_id'); 22 | done(); // Lets supertest this is done so move on. 23 | }); 24 | }); 25 | 26 | afterEach(function (done){ 27 | Book.remove().exec(); 28 | done(); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /server-mongo/controllers/accessController.js: -------------------------------------------------------------------------------- 1 | // https://javascriptweblog.wordpress.com/2010/07/26/no-more-ifs-alternatives-to-statement-branching-in-javascript/ 2 | const accessController = (resolveAccess, getNumber) => { 3 | return { 4 | get: (req, res) => resolveAccess(getNumber)(res) 5 | }; 6 | }; 7 | 8 | module.exports = accessController; 9 | -------------------------------------------------------------------------------- /server-mongo/controllers/accessControllerBusiness.js: -------------------------------------------------------------------------------- 1 | const mockAccess = { 2 | min: 1, 3 | max: 5, 4 | allowedValue: 3, 5 | forbiddenStatus: 403, 6 | notAllowedStatus: 405, 7 | }; 8 | 9 | const mapStatuses = new Map([ 10 | [mockAccess.forbiddenStatus, { message: 'Request rejected' }], 11 | [mockAccess.notAllowedStatus, { message: 'User without priviliges' }], 12 | [mockAccess.allowedValue, { message: 'Granted access' }] 13 | ]); 14 | 15 | const resolveStatusValue = (number) => { 16 | let statusValue; 17 | if(number > mockAccess.allowedValue) { 18 | statusValue = mockAccess.forbiddenStatus; 19 | } else if (number < mockAccess.allowedValue) { 20 | statusValue = mockAccess.notAllowedStatus; 21 | } else { 22 | statusValue = mockAccess.allowedValue; 23 | } 24 | return statusValue; 25 | }; 26 | 27 | module.exports = { 28 | resolveAccess: (getNumber) => (res) => { 29 | const statusValue = resolveStatusValue( 30 | getNumber(mockAccess.min, mockAccess.max) 31 | ); 32 | res.send( 33 | mapStatuses.get(statusValue) 34 | ); 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /server-mongo/controllers/authorController.js: -------------------------------------------------------------------------------- 1 | 2 | const authorController = (Author) => { 3 | const business = require('./authorControllerBusiness')(Author); 4 | 5 | return { 6 | post: (req, res) => { 7 | (req.body.name && req.body.lastName) ? 8 | business.insertAuthor(req, res) : 9 | business.handleNotValidAuthor(res); 10 | }, 11 | get: (req, res) => business.retrieveAuthors({}, res), 12 | } 13 | }; 14 | 15 | module.exports = authorController; 16 | -------------------------------------------------------------------------------- /server-mongo/controllers/authorControllerBusiness.js: -------------------------------------------------------------------------------- 1 | const authorControllerBusiness = (Author) => ({ 2 | insertAuthor: (req, res) => { 3 | const author = new Author(req.body); 4 | author.save(); 5 | res.status(201); 6 | res.send(author); 7 | }, 8 | handleNotValidAuthor: (res) => { 9 | res.status(400); 10 | res.send('Name and Last Name is required.'); // TODO: Move to errors file. 11 | }, 12 | retrieveAuthors: (query, res) => { 13 | Author.find(query, (err, authors) => { 14 | (err) ? 15 | res.status(500).send(err) : 16 | res.json(authors); 17 | }); 18 | }, 19 | }); 20 | 21 | module.exports = authorControllerBusiness; 22 | -------------------------------------------------------------------------------- /server-mongo/controllers/bookByIdController.js: -------------------------------------------------------------------------------- 1 | const bookIdController = () => { 2 | const populateBook = (req) => { 3 | req.book.title = req.body.title; 4 | req.book.author = req.body.author; 5 | req.book.genre = req.body.genre; 6 | req.book.read = req.body.read; 7 | // http://node.green/#ESNEXT-candidate--stage-3--object-rest-spread-properties 8 | // req.book = {...req.body} 9 | }; 10 | 11 | const handleUpdateBook = (req, res) => { 12 | req.book.save((err) => { 13 | console.log(err); 14 | (err) ? 15 | res.status(500).send(err) : 16 | res.json(req.book); 17 | }); 18 | }; 19 | 20 | const populatePatchBook = (req) => { 21 | for (var variable in req.body) { 22 | if (req.body.hasOwnProperty(variable) && !req.body['_id']) { 23 | req.book[variable] = req.body[variable]; 24 | } 25 | } 26 | } 27 | 28 | const handleRemoveBook = (req, res) => { 29 | req.book.remove((err) => { 30 | if (err) { 31 | res.status(500).send(err); 32 | } else { 33 | res.status(204).send('Remove'); 34 | } 35 | }); 36 | }; 37 | 38 | return { 39 | get: (req, res) => res.json(req.book), 40 | put: (req, res) => { 41 | populateBook(req); 42 | handleUpdateBook(req, res); 43 | }, 44 | patch: (req, res) => { 45 | populatePatchBook(req); 46 | handleUpdateBook(req, res); 47 | }, 48 | delete: (req, res) => handleRemoveBook(req, res), 49 | }; 50 | }; 51 | 52 | module.exports = bookIdController ; 53 | -------------------------------------------------------------------------------- /server-mongo/controllers/bookController.js: -------------------------------------------------------------------------------- 1 | const bookController = (Book, bookParser) => { // This way I can inject the Book that I want to test it. 2 | const business = require('./bookControllerBusiness')(Book); 3 | 4 | return { 5 | post: (req, res) => { 6 | (req.body.title) ? 7 | business.insertBook(req, res) : 8 | business.handleNotValidBook(res); 9 | }, 10 | get: (req, res) => { 11 | const query = bookParser.queryParse(req); 12 | business.retrieveBooks(query, res); 13 | }, 14 | }; 15 | } 16 | 17 | module.exports = bookController; 18 | -------------------------------------------------------------------------------- /server-mongo/controllers/bookControllerBusiness.js: -------------------------------------------------------------------------------- 1 | const bookControllerBusiness = (Book) => ({ 2 | insertBook: (req, res) => { 3 | const book = new Book(req.body); 4 | book.save(); 5 | res.status(201); 6 | res.send(book); 7 | }, 8 | handleNotValidBook: (res) => { 9 | res.status(400); 10 | res.send('Title is required'); 11 | }, 12 | retrieveBooks: (query, res) => { 13 | Book.find(query, (err, books) => { 14 | (err) ? 15 | res.status(500).sen(err) : 16 | res.json(books); 17 | }); 18 | } 19 | }); 20 | 21 | module.exports = bookControllerBusiness; 22 | -------------------------------------------------------------------------------- /server-mongo/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'), 2 | nodemon = require('gulp-nodemon'), 3 | gulpMocha = require('gulp-mocha'), 4 | env = require('gulp-env'), // Let us manipulate the environment variables in Gulp, so that we can setup a test environment. 5 | supertest = require('supertest'); // This one allow us to make http calls 6 | 7 | gulp.task('default', function() { 8 | nodemon({ 9 | script: 'app.js', 10 | ext: 'js', 11 | env: { 12 | PORT:8000 13 | }, 14 | ignore: ['./node_modules/**'] 15 | }) 16 | .on('restart', function() { 17 | console.log('Restarting'); 18 | }); 19 | }); 20 | 21 | gulp.task('tests', function (){ 22 | /* 23 | When we get into our app.js I can do a process.env and pull in our environement, 24 | so it'll either be prod, dev or test and that will be governed by Gulp execution. 25 | */ 26 | env({vars: {ENV:'Test'}}); 27 | gulp.src('Tests/*.js', { read: false }) 28 | .pipe(gulpMocha({reporter: 'nyan'})); 29 | }); 30 | -------------------------------------------------------------------------------- /server-mongo/middlewares/bookMiddleware.js: -------------------------------------------------------------------------------- 1 | const bookIdMiddleware = (Book) => (req, res, next) => { 2 | Book.findById(req.params.bookId, (err, book) => { 3 | if (err) { 4 | res.status(500).send(err); 5 | } else if (book) { 6 | req.book = book; 7 | next(); 8 | } else { 9 | res.status(404).send('no book found'); 10 | } 11 | }); 12 | }; 13 | 14 | module.exports = { 15 | bookIdMiddleware 16 | } 17 | -------------------------------------------------------------------------------- /server-mongo/models/authorModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'), 2 | Schema = mongoose.Schema; 3 | 4 | var authorModel = new Schema({ 5 | name: { 6 | type: String 7 | }, 8 | lastName: { 9 | type: String 10 | }, 11 | birthDate: { 12 | type: Date 13 | } 14 | }); 15 | 16 | module.exports = mongoose.model('Author', authorModel); 17 | -------------------------------------------------------------------------------- /server-mongo/models/bookModel.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'), 2 | Schema = mongoose.Schema; 3 | 4 | var bookModel = new Schema({ 5 | title: { 6 | type: String 7 | }, 8 | author: { 9 | type: String 10 | }, 11 | genre: { 12 | type: String 13 | }, 14 | read: { 15 | type: Boolean, 16 | default: false 17 | } 18 | }); 19 | 20 | module.exports = mongoose.model('Book', bookModel); 21 | -------------------------------------------------------------------------------- /server-mongo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rest_node", 3 | "version": "1.0.0", 4 | "description": "Create RESTful service with node express and mongo", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "gulp", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Jaime Salas", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.15.2", 14 | "cors": "^2.8.1", 15 | "dotenv": "^8.0.0", 16 | "express": "^4.14.0", 17 | "gulp": "~3.9.1", 18 | "gulp-mocha": "~3.0.1", 19 | "gulp-nodemon": "~2.2.1", 20 | "mongoose": "^4.6.4", 21 | "should": "^11.1.1", 22 | "sinon": "^1.17.6" 23 | }, 24 | "devDependencies": { 25 | "gulp-env": "~0.4.0", 26 | "supertest": "^2.0.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /server-mongo/parsers/bookParser.js: -------------------------------------------------------------------------------- 1 | const queryParse = function (req) { 2 | // Check if it is an empty object. 3 | // if( Object.keys(query).length > 0) { 4 | // console.log(query); 5 | // } 6 | 7 | const query = {}; // The request query string on JSON format!!! We can do casting on TypeScript 8 | 9 | if(req.query.genre) { 10 | query.genre = req.query.genre; 11 | } 12 | 13 | if(req.query.author) { 14 | query.author = req.query.author; 15 | } 16 | 17 | if(req.query.title) { 18 | query.title = req.query.title; 19 | } 20 | 21 | if(req.query.read) { 22 | query.read = req.query.read; 23 | } 24 | 25 | console.log(query); 26 | 27 | return query; 28 | }; 29 | 30 | module.exports = { 31 | queryParse 32 | }; 33 | -------------------------------------------------------------------------------- /server-mongo/utils/randomNumber.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | getNumber: (min, max) => Math.floor(Math.random()*(max - min)) + min 3 | }; 4 | --------------------------------------------------------------------------------