16 septembrie 2022

javax.validation

Un tutorial util: Spring MVC Custom Validation

Pont: se va adnota cu @Valid fiecare obiect care trebuie verificat (inclusiv in cascada, mergand pana la obiectul final ce contine adnotarea cu constrangere - cum era @ContactNumberConstraint)

--------------------------------

package secret***.controller;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class RandomValidator implements ConstraintValidator<RandomConstraint, String> {

@Override
public void initialize(RandomConstraint contactNumber) {
}

@Override
public boolean isValid(String value, ConstraintValidatorContext cxt) {
return value != null && value.matches("[0-9]+")
&& (value.length() > 8) && (value.length() < 14);
}
}
--------------------------
package secret***.controller;

import java.lang.annotation.*;

import javax.validation.Constraint;
import javax.validation.Payload;

@Documented
@Constraint(validatedBy = RandomValidator.class)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RandomConstraint {
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
--------------------------
@PostMapping("/save")
public ResponseEntity<VrDeResultDTO> save(@RequestBody @Valid ContextDTO contextDTO) {
return new ResponseEntity<>(choreographer.save(contextDTO), HttpStatus.OK);
}
--------------------------
in ContextDTO ---> toate campurile care duc la campul adnotat cu @RandomConstraint trebuie sa aiba @Valid.

08 august 2022

Angular Architecture & Best Practices - ziua 5

Alte considerații

== înlocuire funcții cu pipes - se cheamă doar când se schimbă datele de intrare

{{ product.price | addTax | currency }}

@Pipe({

    name: 'addTax'

})

export class AddTaxPipe implements PipeTransform {

    transform(price: number): number {

        // cod 

    }

}


== Memo Decorator - ajută la caching în funcția de transform a unui pipe, când se trimite un parametru primitiv 

- dacă se repetă parametrii, ține minte rezultatul

- reies mai puține apeluri 

import memo from 'memo-decorator';

...

@memo

transform(price: number): number {

    // cod 

}


Operatori HttpClient & RxJS 

== switchMap: "switch to a new observable" -  după ce primește rezultat de la un Observable, anulează subscripția, trece la următoarea

return this.http.get(url)

         . pipe(switchMap (param1 => {

             return this.http.get(param1['param2']) // conține un url pt a obține date suplimentare

               .pipe (map (res2 => {param1['param2'] = res2; return param1;}))

      }));


== mergeMap: nu anulează inner subscripțiile, permite mai multe, nu garantează ordinea elementelor (concatMap face asta)

return this.http.get(url).pipe(switchMap (...), mergeMap (...), toArray);


== forkJoin: când toate apelurile s-au finalizat, întoarce un array cu rezultatele lor

return forkJoin (this.getA(), this.getB())

          . pipe (map ((res) => {return {val1: res[0], val2: res[1]}}));


Securitate

== CORS (Cross-origin Resource Sharing) - pe server se stabilesc domeniile/porturile de pe care poate primi request (trebuie să limiteze explicit domeniile, header-ele și metodele permise, ex. GET, POST, etc)

== CRSF (Cross-site Request Forgery) 

- pt prevenire se activează CSRF pe server dacă se folosește autentificarea prin cookie-uri 

- Angular va folosi token-ul primit în cookie-urile trimise de server pt a îl adăuga în header-ul de request (doar Angular poate face asta).

== Route Guards - OK, dar nu furnizează securitate

== Transmitere date secrete din Angular: NOK; dar se poate crea un API intermediar care să împacheteze secretul și să-l trimită la API-ul destinatar. Folosire tokeni JWT cu dată de expirare.


Interceptori HTTP

Interceptează requests / responses și atașează date în plus (ex. de tokeni de securitate).

authReq - request-ul îmbunătățit cu noi date

withCredentials - adăugat când se folosesc cookies & CORS

next.handle - predă ștafeta la următorul interceptor, dacă există


Se declară în core.module.ts. 

15 iulie 2022

Angular Architecture & Best Practices - ziua 4

State Management

Opțiuni: servicii, NgRx, ngrx-data, observable store

Altele neabordate: Akita, Ngxs, MobX

Tipuri de stare: specifică aplicației / sesiunii / de business


Servicii Angular

Este cea mai simplă metodă. 

Mai multe servicii pot avea legătură la un storage comun. 


NgRx 

  • reactive state management inspired by Redux; unify the events & derive state using RxJS
    • Redux + RxJS = NgRx 
    • Redux - popularizat de Facebook și React
  • o singură sursă de adevăr în privința stării datelor
  • furnizează date immutable
  • cost: complexitatea codului



// în componentă:

constructor (private store: Store<EntityState>) {} 

this.store.dispatch (new CustomerAction.GetCustomers()); // 1 

// customer.reducers.ts  cheamă servicii 

// customer.effects.ts  este un @Injectable și are metode adnotate cu @Effect care ret. un Observable

// customer.selectors.ts  face subscribe la Store, furnizează rezultatul în componentă


ngrx-data = NgRx simplificat, un wrapper (mai puțin cod - o linie de cod/entitate)


Observable Store

== pachet: @codewithdan/observable-store

== sursă unică a datelor, read only, notificări la schimbare de stare către cei care s-au abonat, păstrează un istoric al schimbării de stare (onBefore, onAfter), cod puțin & funcționează cu orice framework

== inclus într-un serviciu care extinde ObservableStore<TipulDeDate>

== include starea (TipulDeDate) prin this.getState()

Inițializare prin this.setState(data, actionName); // acțiunea se salvează pt logging

== componenta face subscribe la Observable store

12 iulie 2022

Angular Architecture & Best Practices - ziua 3

Comunicarea între componente

Dacă ierarhia componentelor e prea mare (prea multe @Input și @Output), există alternative - ambele se bazează pe subject/observable

  • event bus - injectat în ambele componente care doresc să comunice; componentele fac subscribe la evenimente (nu vor ști de unde vin datele); cuplare slabă;
  • observable service - un serviciu furnizează observables către componente, care pot face subscribe; vor ști de unde vin datele; cuplarea nu e la fel de slabă.


RxJS subjects:

4 feluri principale:

  • Subject (generic) - componentele fac subscribe și pot primi date - doar datele care se emit de la momentul abonării
  • BehaviorSubject - componentele care se abonează mai târziu pot obține date emise anterior (doar la ultima emitere)
  • ReplaySubject - componentele care se abonează mai târziu pot obține toate datele emise în trecut; se poate configura cât să se poată obțină
  • AsyncSubject - trimite cea mai nouă valoare (ultima) atunci când se încheie de tot emiterea.

Convenție: subject - private & observable - public

private subject: Subject<Type>;

observable: Observable<Type>;

// init

this.subject = new Subject();

this.observable = this.subject.asObservable();

// emitere eveniment

this.subject.next(obj);

// abonare

this.observable = this.subject.subscribe (data => { ... });


Event bus: folosește RxJS subjects - o componentă face subscribe la un eveniment, alta emite un eveniment (fără să știe cine va primi datele).

// ambele componente:

constructor (eventBus: EventBusService) {}

// în componenta care emite:

this.eventBus.emit (objOfChoice);

// în componenta care primește:

this.eventBusSubs = this.eventBus.on (EventType, data => {});

Dezavantaje: nu se știe cine declanșează evenimentul; mantenanța este mai dificilă; trebuie făcut și unsubscribe


Observable service

// în componenta care emite (DataService):

this.subject = new Subject();

this.observable = this.subject.asObservable();

this.subject.next (objToSend);

// în componenta care primește:

constructor (dataService: DataService) {}

this.subs = this.dataService.observable.subscribe (data => {});

Avantaje: se cunoaște de unde vin datele

Dezavantaje: cuplarea între observator (componenta care se abonează) și observat (care emite); trebuie făcut unsubscribe


unsubscribe ---> unsubscribe() în ngOnDestroy();

Alte variante: 

1. AutoUnsubscribe decorator - cu npm install 

  @AutoUnsubscribe()

  export class MyComponent implements OnInit, OnDestroy() { }

 // nu este nevoie de nimic în ngOnDestroy (), dar trebuie menționată metoda

2. SubSink - cu npm install 

  subs = new SubSink();

  this.subs = .... // trebuie inițializat cu fiecare subscripție nouă în ngOnInit, se vor adăuga subscripțiile

  this.subs.unsubscribe(); // trebuie apelat în ngOnDestroy

08 iulie 2022

Angular Architecture & Best Practices - ziua 2

 Structuring Components









Container-Presentation pattern: obtine date intr-un singur loc (container) si trimite la componentele de prezentare doar ce este necesar. Comunicare prin @Input / @Output

Child routes: ex. /customers/1/edit - o idee buna pt ca poti face bookmarking


@Output - cu EventEmitter

Complicatie: prea multe niveluri in care trebuiesc pasate datele





  • OnPush: Use the CheckOnce strategy, meaning that automatic change detection is deactivated until reactivated by setting the strategy to Default (CheckAlways). Change detection can still be explicitly invoked. This strategy applies to all child directives and cannot be overridden.
  • Default: Use the default CheckAlways strategy, in which change detection is automatic until explicitly deactivated.
Efectul OnPush: datele primite prin @Input nu se mai pot modifica in componenta dincolo de initializare; in general: cand se schimba proprietatile de @Input si cand se emite un eveniment. 

Recomandare: OnPush pe toate componentele de prezentare. Avantaje: performanta (mai ales cand sunt f multe componente), impiedicare modificare stare.


Clonare obiecte (pt refresh in aplicatie):

1) const obj2 = JSON.parse ( JSON.stringify (obj) ); // deep copy, dar are probleme cu tipul Date

2) serviciu pt clonare, in core:









3) Immutable.js:

> npm install immutable

import * from 'immutable';

immList = List<Obj> (this.objList);

return this.immList.toArray();

SAU

return fromJS (objList).toJS(); 


Mostenire componente: export class ComponentX extends BaseComponent