Sesiuna 2 - Git. Rest API. Javascript. NodeJS

Am atins urmatoarele subiecte:

  • Introducere in Git
  • Strategii de Git Branching
  • REST Api
  • JavaScript

Materiale Online

Resursele pentru a doua sedinta, adica prezentarea PDF si inregistrarile video, pot fi gasite in acest folder de Gdrive

Activitate Practica

Comenzi utile de Git

git clone https://gitlab.com/<your_project> - descarca proiectul cu tot istoricul modificarilor
git init - se initializeaza repo-ul local (se creaza folderul .git)
git remote add origin https://gitlab.com/<your_project> - daca aveti proiectul initializat local cu aceasta comanda il legati la repo-ul din cloud
git status - va arata cateva detalii despre modificarile curente, pe ce branch sunteti etc
git diff - va arata exact care sunt liniile de cod modificate
git add <path_to_changes> - face ca fisierele modificate din locatia specificata sa fie incluse in urmatorul commit, puteti avea si expresii regulate pentru fiererele voastre (ex. * - include toate modicarile din proiect)
git reset <path_to_changes> - face operatia inversa la add, va scoate modificarile de la fisire specificate
git commit -m "SOMETHING" - adaugati un commit local cu modificarile puse la "git add" dinainte cu mesajul specificat (daca nu e folosit "-m" va deschide un editor de fisier sa creati mesajul)
git branch <branch> - creati un nou branch pornind de la commitul pe care va aflati local
git branch -d <branch> - stergeti branch-ul specificat
git push -u origin <branch> - trimiteti toate commiturile locale de pe un branch catre repo-ul din cloud
git pull origin <branch> - va actualizeaza branch-ul local cu modificarile noi din cloud
git checkout -b origin/<branch> - va mutati pe branch-ul specificat cu modificarile din cloud
git checkout -b <branch> - va mutati pe branch-ul specificat cu modificarile locale
git merge <branch> - se incearca aducerea modificarilor de pe branch-ul specificat pe branch-ul curent
git rebase <branch> - mutati toate commit-urile in oridine cronologica de pe branch-ul curent pe cel specificat in comanda
git rebase <destination_branch> <source_branch> - mutati toate commit-urile in oridine cronologica de pe branch-ul surse pe cel destinatie
git stash - va salveaza modificarile curente si revine la starea ultimului commit, util daca vreti sa lucrati la alte lucruri dar nu ati teminat
git stash pop - va aplica modificarile la stash inapoi si le sterge din stocare temporara
git cherry-pick <commit_hash> - va ia toate modificarile de la un anumit commit specificat prin hash-ul sau si le pune pe branch-ul curent
git rebase -i Head~<N> - cu aceasta comanda puteti transforma ultimele N commituri intr-unul singur, in editorul care se deschite trebuie sa inlocuiti "pick" cu "squash" in afara de prima linie

HTTP Rest API

Representational State Transfer (REST) este un stil arhitectural modern care defineste modul in care interactioneaza serviciile web. Acesta se bazeaza pe cereri HTTP (e.g.: GET, POST, PUT, DELETE) pentru a manipula informatia si pe media-types (e.g.: JSON) pentru a stabili cum arata informatia transferata.

Un API de tip REST este un serviciu Web care permite interactiunea prin mecanisme REST. Backendul pe care noi il vom dezvolta la laborator va fi un astfel de API.

Familiarizare Javascript

NodeJS este un mediu de rulare pentru Javascript bazat pe motorul V8 de la Chrome. Acesta permite rularea scripturilor de JS pe calculator, fara sa fie nevoie de browser.

Deoarece backendul si frontendul vor fi realizate folosind tehnologii care se bazeaza pe Javascript, este elementar sa cunoasteti acest limbaj. Javascript este un limbaj de scripting cu o sintaxa foarte usoara, asemanatoare celei din C. Va prezentam mai jos cateva particularitati ale limbajului:

  • JS este un limbaj slab tipat. Variabilele sunt definite nu cu tipuri, dar cu let sau const
  • Functiile nu au un tip de retur si de asemenea nici parametrii nu au tipul atasat
  • Functiile pot fi definite folosind keyword-ul function sau folosind arrow functions
function myFunc(x) {console.log(x)};
 
// este identic cu
 
const myFuncArrow = (x) => console.log(x);
 
myFunc(3); //3
myFuncArrow(3); //3
  • deoarece JS este un limbaj interpretat, nu conteaza ordinea in care sunt definite functiile. Totusi, este indicat sa fiti cat mai organizati in cod
  • JS nu are nevoie de o functie de main ca sa ruleze. Codul dintr-un fisier .js va rula in momentul in care scriptul este apelat
const constanta = 'abc';
let variabila = 3;
 
function adauga (x, y, z) {
  if (x === 'abc') {
    y = y + z;
  }
  return  y;
}
 
const rezultat = adauga(constanta, variabila, 10);
 
console.log(rezultat);
  • in JS exista doua tipuri de egalitate: == si ===. Primul tip de egalitate nu tine cont de tipurile variabirelor comparate, in timp ce al doilea tip este mai strict.
const a = 10; //a este numar
const b = '10'; //b este sir de caractere
 
console.log(a == b); //true
console.log(a === b); //false
 
const c = 'abc';
const d = 'abc';
 
console.log(c == d); //true
console.log(c === d); //true
 
const fals = false; //boolean
const str = ''; //string
const zero = 0; //number
 
console.log(fals == str); //true
console.log(fals == zero); //true
console.log(str == zero); //true
 
console.log(fals === str); //false
console.log(fals === zero); //false
console.log(str === zero); //false
  • JS este un limbaj semi-oop. Cu toate ca are concepte precum obiecte si clase (partial), principiile OOP nu se regasesc in totalitate
  • Vectorii sunt declarati cu paranteze drepte [ ] si obiectele cu paranteze acolade { }
  • Obiectele in JS sunt combinatii de chei valori, unde valorile pot fi orice: variabile, vectori, obiecte si chiar functii
  • Accesul la elementele dintr-un obiect se face in doua moduri:
const obj = {a:1, b:2};
 
console.log(obj.a); //1
console.log(obj['b']); //2
  • Deoarece JS opereaza cu obiecte, exista doua tipuri de copiere: prin referinta, implicit si prin valoare
const obj1 = {a:2};
const obj2 = obj1; // copiere prin referinta
 
obj2.a = 5;
console.log(obj1); // {a:5} -> valoarea s-a modificat si in obiectul original
 
const obj3 = Object.assign({}, obj1); // copiere prin valoare, incomplet pentru nivele mai adanci
 
obj3.a = 10;
console.log(obj1); // {a:5} -> valoarea nu s-a modificat
 
const obj4 = {...obj1} // copiere prin valoare (modern, folosind spread operator, ES6, dar incomplet pentru nivele mai adanci)
 
obj4.a = 9772;
console.log(obj1); // {a:5} -> valoarea nu s-a modificat
 
const obj5 = JSON.parse(JSON.stringify(obj1)); // copiere prin valoare completa
 
obj5.a = ['ana', 'are', 'mere'];
console.log(obj1); // {a:5} -> valoarea nu s-a modificat
  • JS este un limbaj functional. Prezinta concepte precum metode functionale (map, filter, reduce) si functii curry (callback-uri)
const arr = [1, 2, 3, 4];
console.log(arr.map(x => x*2)); //2 4 6 8
 
const obj = { a:2, b:3, c: (x, y) => console.log(x + y)}
console.log(obj.c(obj.a, obj['b'])); //5
 
const func1 = (x, cb) => cb(x);
const func2 = y => console.log(y);
 
func1(3, func2); //3

Familiarizare NodeJS

Prezentare pe scurt

NodeJS este un ecosistem de Javascript care permite folosirea limbajului Javascript pe partea de server. Este bazat pe motorul V8 de la Chrome, ruleaza pe un singur fir de executie si este asincron, utilizand o bucla de evenimente pentru a executa operatiile cat mai rapid.

Structura unui proiect in NodeJS

Module

Un proiect de Node este structurat in module. Fiecare fisier Javascript din componenta unui proiect este considerat un modul. Puteti sa faceti analogia cu fisierele .c din C sau cu clasele din Java.

Un modul poate fi importat de catre oricare alt modul (adica fisier .js), folosind keyword-ul require si calea relativa catre el. Un modul poate expune informatii in exterior folosind proprietatea exports a obiectului global module.

Se pot exporta oricate variabile sau functii. Chiar daca se exporta doar o functie, ca in exemplul urmator, este indicat sa o puneti intr-un obiect atunci cand o exportati

const sayHello = () => {  console.log("Hello world!");  }
module.exports = {
  sayHello
}
/*
module.exports = { sayHello } e identic cu
 
const objForExport = { sayHello: sayHello };
module.exports = objForExport;
*/

Atunci cand creati un obiect, daca cheia si valoarea au aceeasi denumire, se poate scrie doar numele cheii, fara sa se mai puna : si valoarea

modul1.js
const a = 2;
const b = 3;
const getA = () => { return a; }
const getB = () => { return b; }
module.exports = {
  getA
};
// pentru oricine va importa acest modul, singurul lucru la care va avea acces va fi functia "getA"
modul2.js
const myModule = require('./modul1.js');
 
const a_from_module_1 = myModule.getA();
console.log(a_from_module_1); //2
 
const b_from_module_1 = myModule.getB(); //ReferenceError! 
Pachete

Asa cum in alte limbaje se pot folosi biblioteci externe pentru diverse functionalitati ce sunt deja implementate (exemplu, stdio.h in C pentru I/O) si in Node se pot folosi pachete.

Pachetele sunt module instalate din surse externe. Pentru a instala un pachet se foloseste NPM - node package manager in terminal.

Pentru a putea instala pachete intr-un proiect, este nevoie ca aceasta sa fie initializat. Pentru a initializa un proiect de node, este nevoie sa rulati comanda

npm init

In urma rularii acestei comenzi se va crea un fisier, package.json. Acest fisier are rolul de a retine informatii cu privire la proiect si la dependentele sale.

Daca vreti sa rulati proiectul pe o alta masina, este nevoie sa va instalati local toate pachetele pe care le folositi in proiect. Puteti sa le instalati pe fiecare de mana, sau puteti sa va folositi de package.json si ele vor fi instalate automat

Pe langa dependente, in package.json sunt retinute si informatii cu privire la proiect (autor, repository, descriere) precum si scripturi de rulare si testare. Puteti asocia partea de scripts din package.json cu un Makefile.

Dupa ce ati initializat proiectul, puteti incepe sa instalati pachete. Pentru a instala un pachet, se executa comanda urmatoare:

npm install nume_pachet --save

Dupa ce instalati un pachet, se vor crea folderul node_modules si fisierul package-lock.json. In folder se afla toate pachetele instalate. In fisier se retine versiunea exacta a pachetului cu care se lucreaza.

Niciodata sa nu stergeti package-lock.json. De asemenea, acesta trebuie obligatoriu inclus in git.

Pentru a utiliza un pachet instalat, se foloseste tot keyword-ul require, insa se scrie doar numele pachetului, nu si calea catre locul unde a fost descarcat, aceasta deducandu-se automat din node_modules.

//presupunem ca am instalat inainte pachetul fictiv my-awesome-package
 
const myAwesomePackage = require('my-awesome-package');
//do stuff

Pentru a vedea cum se utilizeaza un pachet, este necesar sa cititi pagina acestuia de pe NPM sau de pe site-ul pachetului, daca exista. De obieci, documentatiile sunt cuprinzatoare.

Rularea unui API

Pentru a rula un API de Node, este nevoie sa definiti un fisier de start. Acest fisier trebuie sa includa apoi referinte catre celelalte fisiere ce fac parte din proiect, atat direct, cat si indirect (e.g. start face referire catre modulul 1 iar modulul 1 face referire catre modulul 2).

Exemplu de API minimal cu express:

start.js
const express = require('express');
 
const app = express();
 
app.get('/', (req, res) => {
    res.send("Hello world!");
});
 
app.listen(3000);

Daca doriti sa utilizati scripturi de npm, va trebui sa includeti referinta catre fisierul de start si in package.json:

package.json
{
  "name": "lab2",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node start.js",
    "my-other-start": "nodemon start.js"
  },
  "dependencies": {
    "express": "~4.16.1"
  }
}

Pentru a rula serverul, executati comanda:

npm run start

Puteti sa adaugati oricate scripturi doriti in package.json, in sectiunea scripts. Pentru a rula un script definit, va trebui sa rulati comanda npm run, urmat de numele scriptului.

Atunci cand dezvoltati, va recomandam sa folositi pachetul Nodemon. Acesta urmareste modificari in cod si reporneste automat serverul. In cazul in care vreti sa folositi nodemon, va trebui sa inlocuiti node cu nodemon in package.json.

npm run my-other-start --> va rezulta in rularea folosind nodemon

Testarea unui API

Pentru a testa un API scris in Node, puteti sa folositi cURL, wget sau Postman. Noi va recomandam Postman, pentru ca ofera multe functionalitati utile, precum variabile de mediu, colectii, scripturi automate, etc…

Exercitii

  • Creati-va un grup pe https://gitlab.com/. Numiti-l cum vreti. Puteti sa il faceti public sau privat
  • Creati-va 5 repository-uri in grup. 4 repository-uri vor contine cele 4 microservicii si un repository va avea rol de monorepo.

Un monorepo tine referinta catre alte repository-uri. In el se pot stoca si configurari si fisiere de deployment.

  • Pe repo-urile destinate microserviciilor protejati-va branchul master, incat sa fie accesibil doar prin merge request.

SettingsRepositoryProtected Branches

  • Creati-va inca un branch, de test. Puteti opta sa il protejati, sau nu, depinde de voi.
  • Clonati cele 4 repo-uri care vor tine microserviciile, pe calculator
  • Creati-va cate un proiect de NodeJS in fiecare repo si instalati urmatoarele pachete:
express
helmet
morgan
log-timestamp
express-async-errors
http-errors
axios
dotenv
  • Creati, in fiecare repo, fisierul .gitignore si puneti node_modules in .gitignore
  • Experimentati, cat mai mult, cu NodeJS si Express. Va recomandam sa creati rute folosind obiectul Router din cadrul express.
moby/backend/02.txt · Last modified: 2020/07/21 11:25 by alexandru.hogea
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0