Skip to content
Daniel Álvarez del Reguero edited this page Feb 1, 2025 · 1 revision

Bootcamp Java

Propietario: daniel alvarez Etiquetas: Guías y procesos Fecha: 20 de junio de 2024 GitHub: https://github.com/danipoal/Bootcamp

Comandos Expresiones regulares

tail
cat
grep -i 'mundo$' *O buscar con el Ctrl + f*
	-i = Insensitive para buscar ya sea mayus o minus
	$ = para que esa palabra sea la ultima de a linea
	^\d = Empezar por un digito
	\s = espacios en blanco
	\n = saltos de linea
	ñ.* = Cogeria la ñ y todo lo que va despues
	
[**set](https://www.notion.so/40598087a8f5447eb746acdd9e32d45b?pvs=21) :** [oj] = Que tenga la o o la j pero no juntas
	[^o] = Sombrerito dentro de un set, para buscar todo lo que No contiene la o
	^[oj] = Todo lo que empieza por o o j
	[a-zA-ZñÑ] = Coge todo el abecedario y en mayus, y la ñ porque no esta incluida
	
	o.o = Coge todo lo que sea o_o. Ese _ es cualquiera
	\d = [0-9]
	\D = [^0-9] Lo que no sean numeros
	\b = Para que la palabra se termine justo donde se ponga ese \b
Quantifiers:
	* + ? 
	a{2} = 2 veces a
	s.*o = stackoverflow  -> Codicioso
	
Cajas: () Son los parentesis, y luego en el replace con el dolar $1 $2 moviendolos se puede cambiar el orden de esos grupos
	(\w+)(\s\w+) -> La primera cajua es $1 y la segunda el 2. 

Untitled

GIT

Table of contents -> Para hacer un index
# Intro
This is my first **HTML project**, where i __try my best at CSS__ without having any model to copy, only my imagination and my, not very devoloped creativity.

The CSS is totaly improvised.  
> Una cita  
- Una lista
- Un [link](google.com "Texto si te pones encima") en una lista  

![Imagen no cargada](https://user-images.githubusercontent.com/32896437/153675215-dff3448c-56bc-4da0-9cf1-6a394fd9c6f8.png "Bailando!!!")  
| Columna1 |Columna2 |  
|:---- |:---- |
|A|B|  
~ = /c/Users/user *Ese gusanito significa que estamos en esa ruta*
git config --global user.name "Daniel Alvarez"  *Para identificarte*
										user.mail ""      Si lo pones sin --global sale el user/mail que pusiste
git init /                              [UNTRACKED]
git add -> Se mueve al STAGING AREA     [INDEX]
git commit -> Se mueve al repo LOCAL    [HEAD]        git commit -m "Mensaje"
git push -> Se mueve al repo REMOTO										

*code. -> Abre VSC*

Moverse ebtre commits. 
git log --oneline -> Para ver los logs pero ordenados.
	HEAD -> Es donde esta apuntando la version
	Origin -> Es el repo remoto en gitHug
		git log --all --decorate --oneline --graph -> Para ver las ramas
		**GIT ADOG**
git checkout -> Para moverte entre commits
		checkout main -> Para volver BIEN del todo donde estabamos
git show -> Para ver lo que cambia o no como en github

RAMAS:
git branch rama1 -> Para crear una rama. // **git checkout -b rama02** -> Crea la rama y te posiciona. 2x1 
git merge -> Para unir ramas  // git merge --no-ff ramaX -m"Mensaje"
	Tipos de MERGE
	-Fast FORWARD o no. Con ff se pasa toda una rama como tal al main al hacer el merge.
	 Sin ff queda mas limpio y el el graph se veran las ramas una vez mergeado pero peor para moverte
git rebase -> Una manera de juntar el trabajo pero colocando la rama despues de la del main

El head y el origin. Si el origin estuviera en el de abajo, significa que le debemos un push.

El head y el origin. Si el origin estuviera en el de abajo, significa que le debemos un push.

Untitled

HTML

  • Elementos BLOCK → H1….
    • Seria un display block, que ocupa todo hasta el final horitzontal
  • Elementos LINE

Untitled

<wbr> -> Seria como el salto de linea responsive cuando se hace pequeña la ventana

Medium

Temario de David interesante sobre redes

Api útil para ver los forms en mail

Api útil para ver los forms en mail

CSS

Selectores

Juegos Para aprender selectores

Selectores
	[href]{} -> Por atributo. Todo lo que tenga href se selecciona
	[href^=https]{} -> Todos los que empiecen por https. Se puede con $
	[class|= heading-]{} -> Todo lo que contenga en la clase heading-
	
	main > div > h2{} -> Seria escoger a los h2 que estan en un div que estan en un main
	main > div > .azul -> Igual que antes pero en vez de h2 los que tengan clase .azul
	main + div -> Que esten en el mismo nivel (hermano) Pero coge los div que van despues de un main
	main ~ div -> Todos los hermanos h2
	
Pseudo Clases
	:hover
	::first line -> Coge lolo la primera linea
	div > div > span + code:not(.foo)-> Un code que este dentro de dos div y vaya detras de un span, que no sea de clase foo
	:last-of-type :only-of-type, nth-of-type(), 
	:last-child, :nth-last-child(), ...
	

Untitled

Column-rule: !important herencia(initial, inherit)

transform: translateX(50px); → Coge el contenido y lo mueve a la derecha del div como un padding

Box-sizing

1em = 16px
margin/border/padding/content
**Para pintar un padding:**
	h1{
		--padding: 20px; -> Al hacer -- se guarda la variable
		padding: var(--padding);
		box-shadow: inset 0 0 var(--padding)
	}
border: 1px solid blue; -> border-width, border-style, border-color (son tambien shorthands)
Elementos inline se puede margin lateral pero no arriba abajo

width y height -> Content (por defecto un box-sizing: content-box)
Box-sizing: border-box; -> Cambia el heigh i width de content a border+padding+content

Display/Position

overflow:
	Si tu a un div le das height y width, si el contenido se sale se produce un overflow. 
Margenes colapsados:
	Los margenes cuando chocan siempre se cogera el mas grande
Display: 
	inline -> ***En el inline, si le pones margin solo afecta los laterales!***
	block ->
	inline-block -> Sigue inline, pero hace caso arriba y abajp
	flex -> 
	None -> 
Outline: 
	Un borde por fuera del border. No desplaza nada. outline-offset: Lo hace para dentro
	Aunque empieza en el border.
	outline-offset: Distancia del borde al outline
box-shadow: Se pueden poner varios poniendo *comas*
Position:
	static: Valor por defecto. El top, bottom... no funcionan. Todos los demas valores de position no son static
	relative: Relativo al posicionamiento inicial. Usar top, bottom.. Cuando este se mueve, nadie ocupa su posicion inicial
	absolute: Si se mueve ocupan su posicion inicial, tmbn relativo al padre pero a la izq arriba
						Tambien crea por defecto un display block
	sticky: Hace falta un top o algo
💡 Importante: Si pones el container en `position Relative`, y el hijo en `position Absolute`, el hijo siempre se encontrara dentro de el padre pero con esa posición absoluta.

Float

Float/clear  *(No se usa ahora, pero esta bien saberlo x si lo encuentras)*
	float: right;
	clear: both;
Units:
	Absolutas -> CM/PX/.. No cambian segun dispositivos o pantallas
	Relativas-> %/ Si cambian
		EM -> Se coge el tamaño relativo de el padre directo en cascada
		REM -> Se coge el font-size de el navegador, predeterminado a 16px
		% -> Coge width pero NO height
		ViewPort -> Como el % pero en funcion de la ventana visible del navegador
			Height: 100vh - Width :100vw
			Mirar vmax Vmin
Color:
	Transparent: 
Media-Querry:
	@media (max-width) and (250px <= width <= 400px)
	

Flex-Box

css-flexbox-poster.png

flex.pdf

Container:
	**display**: flex;
	justify content
	align-items
	flex-direction -> Column / row(default)
	gap -> Espacio entre los hijos
	wrap -> noWrap(default) / wrap -> No se hacen pequeños al modificar pantalla y se mueve debajo
														reverse -> Sube pero arriba
	flex-flow = [flex-direction, flex-wrap]
	align-content: Como items pero para cuando hay mas de una fila
	
Child:
	ORDER -> order: -1; (Ese iria delante de todos). Si empata el order, va el del html
	align-self -> Como el items pero va en el hijo y para ese objecto en concreto

GRID

Container:
	display: grid;
		grid-template-columns: 1fr 2fr 1fr; == 25% 50% 25% de la cuadricula 3 columnas
		grid-template-rows: 1fr 50px 10vh 2fr; = 4 columnas
											: repeat(5, 1fr)
		grit-template: template-rows / template-columns
		grid-row-gap:;
Los elementos se van posicionando en la cuadricula. Para posicionarlos menualmente:

Child:
		grid-column-start: 1; grid-column-end: 3; Se hace el shorthand debajo/Empieza 1 acaba 3(no incluido)
		== grid-column: 1 / 3; == grid-column: 1 / *span* 2; -> El span es no que acabe en x sino que ocupe X
			    Si pones -1 -> Se selecciona hasta la ultima column/row
		GRID-AREA: row-start/column-start / row-end/column-end;
		order -> tambien se puede escoger el order de los elementos
		0fr -> Hace un fit-content

Frontend Mentor | Front-end coding challenges using a real-life workflow

Web para mejorar el front con ejemplos muy buenos

Preprocesadores Sass

Hay que tener node y despues npm i -g sass
A continuación tener un archivo .scss en el que puedes anidar selectores tal que:}
	
	div{
		.container{
		}
	}
Entonces instalas un plugin para preveer sass.
Se escribe en el archivo scss. Despues compilas el archivo y automaticamente se introduce
en el archivo css original con:
	sass archivo.scss style.css
Si quieres que se compile cada vez que haga un cambio automaticamente
	sass -watch archivo.scss
💡 Utilización de :**target** Cuando queremos que se produzca un evento al pulsar un , lo que hay que hacer es, -Introducir un id en el elemento en el que queremos por ejemplo que desaparezca o se coloree. -Hacer referencia en el a ese id poniéndolo en el `href`. -Poner la clase del id, con el sufijo 🎯 `:target`

Por ejemplo:

<div **id="cerrar-popular"** class="popular-container">
    <a class="popular-close" **href="#cerrar-popular"**>
	     <img style="height: 15px;" src="./images/close.png" alt="Error">
	   </a>
</div>
#cerrar:target{
  display: none;
}

JavaScript

Vanilla

Hay que poner siempre el script en la parte inferior de el body para que carguen primero los elementos. Sino se pueden usar defer o async .

console.log(`${variable1} es una variable`);

Data types:

  • String

  • Number

  • Boolean

  • Null → Alguien tiene que ponerlo explícitamente x = null; x= {};

  • Undefined → Una variable sin valor

  • Symbol → const x = symbol(”Hola”);

  • Object → key: value,

    • Arrays → let arr = [1,2];

LOOPs

💡 En un forEach hay siempre 3 parámetros : → `array.forEach((item, posicion, array) ⇒ {})` Estos indican el elemento, el contador y todo el array o elemento a recorrer en si

Funciones

function funcion(){}
Arrow function->
	const function = (params = defaultParam) => {}
	*//Para poder poner todos los parametros que quieras, con los 3 puntos*
	const Multiples params = (**...params**)=> {} 
	IF EN UNA LINEA:
	n1 > n2 ? console.log(n1):  console.log(n2);
Operadores:
	== -> Compara valores aunque sean string o boolean "true" = true
	=== -> Compara todo true != "true"
	
	Para pasar de string a number -> + string
	Para pasar de number a string -> "" + number
	++numero != numero++ -> Uno suma antes y otro suma despues, si tiene un log se imprime diferente
	
	FOR OF -> Arrays, colecciones...
	FOR IN -> propiedades, atributos...
	
	MAP vs FOREACH -> Map devuelve un dato directamente mientras que each simplemente es un for
💡 En una función para meter un array en los parámetros y que coja estos en orden según el array se hace con el operador `…`
const array = ["azul", 22, false];
function miFuncion(color : string, edad : number, hombre : boolean){
	}
miFuncion(***...array***);

Con esto podemos meter un objeto de array directamente en el parámetro y que los elementos de este ocupen sin problema los elementos de 1 en 1 de la función.

String-Array Métodos

Untitled

Untitled

Untitled

array**.filter**(elementos ⇒ condicion) → Se coloca el array, y se generara uno nuevo si se cumple la condición que pongamos, no hace falta poner el if para la condición. No itera.

array**.map**(elem,index,array ⇒ función) → Recorre el array y ejecuta la función para cada elemento sacando luego un return automático. El resultado se guarda en otro array

array**.forEach**(elem,index,array ⇒ función) → Recorre el array como el map, pero no devuelve nada, es un for normal y corriente

numbers**.reduce**( (accumulator, currentValue) => accumulator + currentValue , 0); → Recorre el array y hace la función que le digas para todos los números. En cada iteración se coloca en current y después el acumulador es el total. En este caso se suma y el 0 es el valor inicial de acumulador.

✅ En estos métodos, cuando hay una condición que se tiene que cumplir para que devuelva algo, muchas veces esa condición tiene que ser `===` y no de valor `==`

JQuery

Para importar jQuery, hay que descargar el archivo .js que seria como una librería, e importarlo en el index .html

Si pones document en el console, te coge da el html

DOM / BOM

JS
	Seleccionar elementos en js -> querrySelector() / getElementBy()
		const parafo = document.querrySelector(".java / #id"); //Pero solo coge una cosa, si hay mas hay q susar qS..All
	
	Editar el texto de HTML con js -> textContent / innerHTML - *Van sin ()*
		parafo.innerHTML = "<h1>Añadimos html a saco</h1>"; 
		parafo.textContent = array;
	Obtener valores ->
		De un input -> input.value *Es su atributo .value
	TOGGLE ->* Para añadir clases a elementos de el html *No hay toggle directo, se hace a mano*
		myButton.classList.contains('activo') ? myButton.classList.remove('activo') : myButton.classList.add('activo');
		-> myButton**.classList.toggle**('nombreClase')  - *Asi si que se podria directamente*
	**Para cambiar css desde JS** -> **elemento.style*.propiedadCSS*** = x
		-> *main.style.backgroundColor = color.value -* Se cambia a traves de el valor de un input
		
        
   
JQUERY
	Seleccionar elementos en js -> $()
		const articles = $('article');  -> Por tag name
										 $('#id_XX');
										 $('.clase');
	Editar el texto de HTML con jQuery -> html() / text() - *Van con parentesis*
		articles.html("El articulo x es como..."); Cambia el contenido
		articles.text("El texto es este"); 
		parrafo.style.backgroundColor("red");
	Obtener valores
		De un input -> .val()
	Creando elementos:
		document.createElement - .createTextNode	
	Para añadir/quitar una clase
		.toggleClass('clase') 
		

element**.addEventListener**(event, function, options) → Se añade al elemento un escuchador en el que para el evento {’click’ o ‘mouseover’ o ‘’}, ejecutará la función. En la parte de la función abrir { } y el options es opcional. *En jQuerry es **.on()***

input.addEventListener('keydown', (***evento***) ⇒{}) → También se puede hacer un Listener cuando pulsamos una tecla. En este caso, habrá que señalar posteriormente que tecla ha sido event.key

APIs

Para interactuar con la API de Pokemon:


TypeScript

Viene a ser un JS pero con tipado fuerte. Se tiene que compilar a JS para que lo ejecute el navegador.

Para instalar → npm i -g typescript

Comprobar npm root -g → ruta

Para ver versión compilador → tsc -v

Compilar → tsc archivo.ts

Configuración ts → tsc --init

Compilación constante → tsc --watch

🐊 En el archivo de configuración ts, puedes aprovechar para hacer que en una carpeta de el proyecto se deje el js y en otra el ts.

Conceptos Básicos

Cuando cogemos los elementos de un querySelector, tenemos que definirlos de el tipo que son para evitar luego problemas en tiempo de programación.

Ejemplos:
	let consoleOutput = document.getElementById('output') as **HTMLParagraphElement**;
	let consoleInput = document.getElementById('input') as **HTMLInputElement**;
	const canvas = document.getElementById('canvas') as **HTMLDivElement**;

También en las funciones hay que especificar el tipo de cada parámetro y cada return .

const randomString = (frases : string) : **number** => {
		let numero = +"12";            //Con el operador + se comvierte un string en un numero
	return numero;
}

Como añadir propiedades nuevas a los elementos del DOM o a objetos:

Los elementos como HTMLImageElement tienen propiedades propias como que tienen valores por defecto o se pueden crear nuevas propiedades en función de las necesidades.

Propiedades ya creadas:
	p.textContent = "x";
Propiedades personalizadas:
	*// Definimos una interfaz que extienda HTMLImageElement para incluir isSelected como atributo* 
	**interface SelectableImageElement extends HTMLImageElement {**
	  **isSelected**: boolean;}
	  
	const cartas = document.querySelectorAll(".carta");
	cartas.forEach((carta){
	 const selectableCarta = carta as SelectableImageElement;  *//Aplicamos la interfaz para que disponga de isSelected*
   selectableCarta.isSelected = false;                       *//Decimos que inicialmente es false*

API fetching *en TypeScript*

Hay que descargar postman y revisar que el get da el json correctamente, en este iremos a la sección de codeSnippets para obtener el código mas sencillo que nos devuelve dicha información. Hay opciones para todos los lenguajes

Código básico de fetch ts:

*//La interfaz es opcional de typescript, pero asi es la estructura de lo que devuelve la API*
interface JokeResponse {
    categories: string[];
    created_at: string;
    icon_url: string;
    id: string;
    updated_at: string;
    url: string;
    value: string;
  } 
let jsonResponse: JokeResponse | null = null;    *//Declaramos el objeto de la interfaz con todo null de momento*

**const requestOptions: RequestInit = {
    method: "GET",
    redirect: "follow" as RequestRedirect};

  fetch("https://api.chucknorris.io/jokes/random", requestOptions)
    .then((response) => response.json())
    .then((result: JokeResponse) => saveResult(result))
    .catch((error) => console.error(error));**

    const saveResult = (json : JokeResponse) => {
        jsonResponse = json;
        console.log(jsonResponse);}

Paso 1: Declaración de requestOptions

  • requestOptions: Es un objeto de configuración que contiene las opciones que se utilizarán en la solicitud fetch. Se coloca en el segundo parámetro de este.
  • RequestInit: Se declara explícitamente como RequestInit, que es una interfaz en TypeScript que define las opciones para una solicitud de fetch.
  • method: "GET": Especifica que el método HTTP de la solicitud será GET. Esto significa que estamos solicitando datos del servidor sin enviar ningún dato en el cuerpo de la solicitud.
  • redirect: "follow" as RequestRedirect: Especifica cómo se deben manejar las redirecciones. El valor "follow" indica que fetch seguirá automáticamente cualquier redirección. Se está utilizando as RequestRedirect para asegurar que el valor es de tipo RequestRedirect.

Paso 2: Uso de fetch con requestOptions

  • fetch(url, options): Se llama a la función fetch con dos argumentos:
    • url: La URL del recurso al que se quiere acceder, en este caso, "https://api.chucknorris.io/jokes/random".
    • options: El objeto requestOptions que contiene las configuraciones de la solicitud. Es opcional.
  • then((response) => response.json()): fetch devuelve una promesa que se resuelve con un objeto Response. Aquí, estamos utilizando el método .json() del objeto Response para convertir la respuesta en formato JSON. Esto también devuelve una promesa que se resuelve con el contenido del JSON.
  • then((result: JokeResponse) => saveResult(result)): Una vez que la promesa de .json() se resuelve, se llama a esta función then, que recibe el resultado del JSON (de tipo JokeResponse). Luego, pasa este resultado a la función saveResult.

Al ser un ejemplo tan sencillo, he colocado el console.log() dentro de la función, ya que si se coloca fuera este dará undefined. Esto es debido a que el fetch tarda mas de lo que se tarda en llamar al log y se ejecuta antes de que se haya guardado el valor.

🤠 Para evitar este error, hay que **colocar el fetch en una función** y la así la llamaremos cuando queramos y dejaremos que vaya cargando anticipadamente.
  • Funciones y utilidades

    .then es la manera de hacer una respuesta asíncrona, y que cuando termine la función que va delante, haga lo que hay en su callback function. Se pueden encadenar, y cada .then recoge el valor que retorna el anterior.

Bootstrap

Librerías de CSS

Bulma → Moderno y sencillo

Materialize → Estilos google

Foundation → Plantillas

Tailwind → Templates + CSS en clases

**Bootstrap** → Componentes

Nes.css → Pixel Art

  • Set up en local npm

    npm init -y → Para generar el json de dependencias

    npm i bootstrap → Para generar el node_modules - Hay que referenciar en .gitIgnore

    Las dependencias estarán en los archivos package.json y el lock. En este cuando en una versión salga ^, significa que esa no se actualizará.

    En el HTML → importar

     <link rel="stylesheet" href="./style.css">
     <script src="../../node_modules/bootstrap/dist/js/bootstrap.bundle.js"></script>

    En el SCSSSi quieres editar el propio contenido Bootstrap

    @import '../../node_modules/bootstrap/scss/bootstrap.scss';
    //Aqui modificaremos el codigo scss y se procesara para los cambios en css
    @import '../../node_modules/bootstrap/scss/functions';
    @import '../../node_modules/bootstrap/scss/variables';
    @import '../../node_modules/bootstrap/scss/mixins';
  • Set Up CDN

    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>

Media Queries mixins

//Uso :
@include media-breakpoint-only(sm){ -> Aplica solo en este punto
    body{
        background-color: red;
    }
}
media-breakpoint-beetwen(md, lg) -> Entre estos dos tamaños
media-breakpoint-up(md)          -> A partir de el breackpoint hasta infinito
media-breakpoint-down(md)        -> Hasta este punto inclusive

Como podemos ver, si definimos un sm, este va desde los 566 hasta el infinito si no subimos

Como podemos ver, si definimos un sm, este va desde los 566 hasta el infinito si no subimos

Untitled

Hay containers que ocuparan casi todo su padre menos algunos pixeles y container-fluid que ocupa 100%

La paleta de colores se puede modificar ya que los valores que tienen son !default, osea que si no se asignan toman el valor que le daremos.

CLASES PREDETERMINADAS:
	text-muted -> Da como una pequeña opacidad
	display-1 -> Como si hucieras un h1 sin ponerlo
	text-center -> align-text
	list-unestyled -> style:none en la lista
	opacity-25 -> Opacidas
	bg-primary - bg-warning - bg-danger - 
	me-1 ms-1 mx/my ... pe/ps/px/py -> 
	align-self-center -> (Hay que darle tamaño al **row**)
	jsutify-content-center -> La row no debe estar ocupada al completo 

Grid System

  1. Hacemos un .container
  2. div con row
  3. div con col→ Se dividen en 12. Si se declaran 2 pues cada una ocupa 6/12

Borders:

Hay que añadir border

Después puedes usar los demás elementos : *border-3 border-warning rounded*

    <div class="container">
        <div class="row">
            <div class="col">Ocupa 1/12</div>
            <div class="col">Ocupa 1/12</div>
             <div class="col-xl-10 col-2">Ocupa 2/12 y en mediaQuery xl 10/12</div> 
             <div class="col-2 me-1 mx-1">Ocupa 2/12 con 1 columna de **margin** en end y start</div> 
		         <div class="col">Ocupa 1/12</div>
        </div>
    </div>
    Si tiene tamaño como col-2 y le añades margin, es probable que haga wrap y desborde
    Si solo tiene col sin definir lo que ocupa, no hace wrap
    - col-xl-10
		- col-auto → *Ocupa todo el espacio que necesite el contenido*
		- order-1
		- offset-4 -> Ese elemento dejara delante un espacio de 4 **vacio**
		- Gutter = *gy gx con valores de 0 a 5 y deja espaciado como gap [Rarete no va mu bien]*
https://www.notion.so/icons/info-alternate_purple.svg También en el row se puede definir cuantas columnas puede tener. Usamos **`row-cols-1 row-cols-md-3`** Esto definirá solo 3 columnas cuando la pantalla sea mediana a grande y para móvil solo 1. *Así también podemos hacer uso de los margin con las col*

Dimensiones

  • h-25 - h-50 → Ocupa el % del padre
  • w-50 → Igual pero se hace en container

Imágenes

Con container se hace responsive, con container-fluid se centra + responsive

img-fluid img-thumbnail

float

table table-info table-striped table-hover table-active table-border

d-none d-sm-block

Se trata de un botón o que abre una ventana emergente ya sea para login o algún formulario.

<!-- Button trigger modal -->
<button type="button" class="btn btn-primary" data-bs-toggle="modal" **data-bs-target="#exampleModal**">
  Launch demo modal
</button>
//El ID tiene que coincidir
<!-- Modal -->
<div class="modal fade" **id="exampleModal"** tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header">
        <h1 class="modal-title fs-5" id="exampleModalLabel">Modal title</h1>
        <button type="button" class="btn-**close**" data-bs-dismiss="modal" aria-label="Close"></button>
      </div> //ahi esta el boton para cerrar, en el mismo div que el titulo
      <div class="modal-body">
        ...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>
📄 Para el login tendremos que hacer un **form** con la **clase** [`needs-validation`](https://getbootstrap.com/docs/5.3/forms/validation/). *Para el label y el input las clases: form-label y form-control*
  • Ejemplo de validación

    <form class="row g-3 needs-validation" novalidate>
      <div class="col-md-4">
        <label for="validationCustom01" class="form-label">First name</label>
        <input type="text" class="form-control" id="validationCustom01" value="Mark" required>
        <div class="valid-feedback">
          Looks good!
        </div>
      </div>
      <div class="col-md-4">
        <label for="validationCustom02" class="form-label">Last name</label>
        <input type="text" class="form-control" id="validationCustom02" value="Otto" required>
        <div class="valid-feedback">
          Looks good!
        </div>
      </div>
      <div class="col-md-4">
        <label for="validationCustomUsername" class="form-label">Username</label>
        <div class="input-group has-validation">
          <span class="input-group-text" id="inputGroupPrepend">@</span>
          <input type="text" class="form-control" id="validationCustomUsername" aria-describedby="inputGroupPrepend" required>
          <div class="invalid-feedback">
            Please choose a username.
          </div>
        </div>
      </div>
      <div class="col-md-6">
        <label for="validationCustom03" class="form-label">City</label>
        <input type="text" class="form-control" id="validationCustom03" required>
        <div class="invalid-feedback">
          Please provide a valid city.
        </div>
      </div>
      <div class="col-md-3">
        <label for="validationCustom04" class="form-label">State</label>
        <select class="form-select" id="validationCustom04" required>
          <option selected disabled value="">Choose...</option>
          <option>...</option>
        </select>
        <div class="invalid-feedback">
          Please select a valid state.
        </div>
      </div>
      <div class="col-md-3">
        <label for="validationCustom05" class="form-label">Zip</label>
        <input type="text" class="form-control" id="validationCustom05" required>
        <div class="invalid-feedback">
          Please provide a valid zip.
        </div>
      </div>
      <div class="col-12">
        <div class="form-check">
          <input class="form-check-input" type="checkbox" value="" id="invalidCheck" required>
          <label class="form-check-label" for="invalidCheck">
            Agree to terms and conditions
          </label>
          <div class="invalid-feedback">
            You must agree before submitting.
          </div>
        </div>
      </div>
      <div class="col-12">
        <button class="btn btn-primary" type="submit">Submit form</button>
      </div>
    </form>

BD

Generador de datos → https://generatedata.com/

https://www.notion.so/icons/info-alternate_purple.svg admin admin

root admin

Reverse engineer → Para ver el UML

Unsigned → Quita todos los valores negativos y los coloca encima como positivos, duplica los enteros

https://www.notion.so/icons/swap-horizontally_pink.svg `USE mydb` → Se pone en **negrita** en schemas

DESCRIBE tableName → Para ver todo lo que contiene SHOW COLUMNS FROM tableName

MaxSize → SHOW VARIABLES LIKE ‘max_allowed_packet’ El máximo de peticiones

Categoría Comandos Principales
DDL (Data Definition Language) CREATE, ALTER, DROP, TRUNCATE, RENAME
DML (Data Manipulation Language) SELECT, INSERT, UPDATE, DELETE
DCL (Data Control Language) GRANT, REVOKE
TCL (Transaction Control Language) COMMIT, ROLLBACK, SAVEPOINT, SET TRANSACTION

Queries

INSERT INTO mydb.tableName VALUES (1, 'Adrian', DEFAULT);
			-> Sabes que atacaras 100% a mydb, te curas en salud
INSERT INTO  mydb.tableName(nombre) VALUES ('Adrian');
			-> Si id AUTOINCREMENT y DEFAULT es Spain, adrian tendra esos campos igual
ALIAS:
	SELECT columnaName AS **columnNombre** FROM mydb
			-> En la vista se vera el **alias**, se puede hacer sin el AS
TRUNCATE:
			-> Como el delete pero resetea los id de a tabla
OPERADORES:
	SELECT * FROM mydb WHERE columnName **LIKE** 'Dan%l';
		-> Encuentra lo que empiece por DAN y acabe por l
	SELECT * FROM mydb WHERE columnId **BEETWEEN** 100 AND 200;
	IN:
		-> Seria como el or pero puede ir en parentesis un array de posibilidades.
		SELECT * FROM mydb WHERE columnId **IN** (200,120); *200 OR 120*
SUBQUERY:
	SELECT * FROM myTable WHERE id **IN** (SELECT id FROM myTable WHERE country = 'Germany' OR country = 'Turkey');
		-> Encuentra todo where el id solo tenga los paises de alemania...
	**ORDER BY** -> LIMIT y OFFSET *'El offset es para especificar donde empieza a contar'*
	**GROUP BY** -> Necesita una operación en el select.
		SELECT COUNT(personas_Name) FROM tabla GROUP BY oficios;

Dentro de un select se pueden poner operaciones entre (id + 200).

Relations

JOIN:
	-> Va antes de el where y contiene un on para matchear las columnas de diferent tables
	INNER-> SELECT * FROM table1 **INNER JOIN table2 ON table1.id = table2.fk_Id**
		-> Para juntar dos tablas relacionadas con fk
	OUTER *(LEFT, RIGHT, FULL)*-> TableA y TableB : (LEFT junta en la A), (RIGHT en la B), (FULL junta todo) 
		-> Se mantienen las columnas en la que se junta
		SELECT * FROM employees **LEFT JOIN** Buildings ON Employees.building = Buildings.Building_name WHERE Employees.name NOT NULL;
	UPDATE:
		-> Actualizar registros. 
		UPDATE tabla SET nColumna = 'valor' WHERE nColumna = 'Paco'
UNION: Permite unir tablas que no tienen ninguna relación

Transactions

SHOW VARIABLE WHERE Variable_name = 'autocommit'; *Ademas es uno de los botones de arriba* 
	-> SI esta en ON: se commitea todo lo que se hace
	-> Si esta en OFF o 0: con el COMMIT despues de un Begin
BEGIN
	-- operaciones (un delete)
**COMMIT o ROLLBACK**

Java

byte→ 256 valores posibles = {-128, 127} - 8 bits en binario

short → {-32.768, 32.767}

long → Se pone una L al final de el valor

continue → Saca de esa iteración pero solo esa y sigue iterando las demás

break → Te saca y no hace ninguna iteración restante

https://www.notion.so/icons/info-alternate_purple.svg La diferencia entre los operadores OR `|` y `||` es que el primero es sin cortocircuito, osea si la primera comparación es true, se sigue evaluando la segunda. El segundo operador, el usualmente usado, cuando encuentra el true ya no hace nada. *De manera que `|` se usa normalmente cuando hay una función a evaluar que tendrá diferentes acciones también.*

Lo mismo pasa con &&. Si el primero es false ya no se evalúa nada mas.

Los Wrappers son clases que envuelven a los datos primitivos que nos permiten tratar a estos como objetos y por tanto usar sus funciones.

Clases abstractas → clase base/padre que no se puede instanciar.

Enum → Para constantes

public enum Futbolista{
	DEFENSA,
	DELANTERO,
	PORTERO
	} //Se podria usar para un checkbox que tiene 4 valores
	
	Futbolista ramos = Futbolista.DEFENSA;

Maps

Map keySet → Objeto como un [] infinito que contiene objetos clave-valor.

	public String allNames(@RequestParam Map<String, String> parametros) {
		String output = "";
		for(String key : parametros.keySet()) {
			output += "Clave: " + key + " Valor: " + parametros.get(key) + "<br>";
		}

Sabemos que la clave y valor son Strings porque se define <String,String>

Para crear → Map<String, String> map = new HashMap<>();

Para recorrer → parametros.keySet() y después para el valor objeto.get(key)

Este for se trata de un forEach en java: (llave: llaves)

JDBC

Creamos la conexión con la BD

Para interactuar con ella siempre hay que crear un Statement a partir de la conexión.

SWING

JFrame → La ventana en la que meteremos todo.

Es invisible y sin tamaño, así que hay que darle dichas características.

setVisible, setSize, setTitle, setLocation, setBounds, setResizable, …

Layout:

Frame → .setLayout(new BorderLayout()); + frame.add(objeto, BorderLayout.NORTH)

AZURE

Capex gasto de golpe

Opex gasto de suscripción

TCO total cost ownership, lo que cuesta mantener un negocio

SPRING

  • Set Up TODO

    Spring Boot tools para eclipse en el market place, y puedes usar un initialzr o directamente en el propio eclipse.

    Hay que descargar el archivo binario de maven y guardar el maven home

    Después en eclipse → Windows/Preferences/Maven/Instalations → Añadir el directorio de el binario descargado para que sea un maven general.

    En el mismo sitio, en Maven/User settings → Hay la ruta de el archivo donde se guardan las dependencias de maven .m2/dependencies.

Cuando se inicia →

https://www.notion.so/icons/info-alternate_purple.svg Hay que darle al 🛑 cuando quieres volver a iniciar para que el puerto 8080 se quede libre y que funcione otra vez. O sino, descargar la dependencia de Spring Boot devTools, que hace cada vez que se editen los archivos regenere todo automáticamente.

Loggers

	private static final Logger LOGGER = LoggerFactory.getLogger(IniciadoAquiApplication.class);
		LOGGER.info("Mensaje");

En application.properties

https://www.notion.so/icons/info-alternate_pink.svg También se puede hacer configuración para lo que sale en la web en un 404 o que en el log no haya lo de Spring. Todas estas configuraciones, se pondrán luego automáticamente en el pom.xml
logging.level.root = OFF / TRACE / INFO
logging.level.com.example = x
logging.file.name = “C:/ruta/archivo.log” → Se llena ese archivo con todo un log 
logging.pattern.console = “” → Para identificar como quieres que se vean los logs .console o .file
server.port = 1212

API REST

Swaggerhttps://swagger.io/

JSON Crack → 🌴 Para ver el esquema árbol de la información de un json

CheatSheet de Revel para Spring Annotations

CheatSheet de Revel para Spring Annotations

@SpringBootAplication → 3x1

El ComponentScan analiza el nombre de los paquetes→ com.example.nombrePaquete

Lo primero que se inicia es el DispatcherServlet después el controller que hagamos

Para el controlador:

La clase la hacemos con la anotación del controlador que escojamos.

@Controller / @RestController (+ResponseBody)

A continuación, se hará una función que devuelva un String con la anotación de el request del endpoint que decidamos. Se podría usar modelAndView pero es antiguo, se podría poner un http estatus manualmente.

**@Controller**
public class HomeController {
	@RequestMapping("/")
	public String index() {
		return "index.html";  //La web estaría en resources/static
	}
	**@ResponseBody**
	**@RequestMapping**(path = "/json", produces= "application/json")
	public String indexJson() {
		return "{\"name\": \"Dani\"}";
	}
}

En el RestController, cuando devuelve un ResponseBody, significa que no devolverá una pagina web, sino un body con un json u otra información. Este se puede poner en la clase o en la función.

JSON → En el return hay que usar la ** para escapar las comillas e indicar que es un json con un produces. También se puede hacer un objeto envolvente

  • JSON a partir de objeto encapsulado

    Creamos la clase y luego la introducimos en la función de el controlador

    	@ResponseBody
    	@RequestMapping(path = "/tx")
    	public TextToJson textJson() {
    		return new TextToJson("aa");
    	}
    package com.example.pojos;
    public class TextToJson {
    	public TextToJson(String message) {
    		this.message = message;
    	}
    	public String getMessage() {
    		return message;
    	}
    	public void setMessage(String message) {
    		this.message = message;
    	}
    	private String message;
    }

Para mapear el Json se puede hacer a través de: Jackson, Gson, Json-b. (json formatter en el navegador en Chrome Marketplace)

Para especificar el status code se hace a través de un ResponseEntity . Esto se puede hacer con una función que retorne o directamente con una anotación.

  • Código ResponseEntity

    @RestController
    @GetMapping("/")
    public ResponseEntity<TextToJson>(){
    	TextToJson recurso = new TextToJson("mensaje");
    	return ResponseEntity.status(HttpStatus.I_AM_A_TEAPOT).body("recurso");
    } //Devuelve un 418 con el body de el json que le hemos pasado
    ***//or (+ las anotaciones de antes)***
    
    **@ResponseStatus**(HttpStatus.ACCEPTED)
    public String respuesta(){
    	return "web.html"
    }

En el RequestMapping puedes poner dos valores o mas y servirán para esos 2 endpoint . De manera que → RequestMapping(value = {”/junio”, “/agosto”})

Parámetros

Usamos la anotación @RequestParams y estas se pueden recibir a través de un GET o POST.

	@RequestMapping("/welcome")
	@ResponseBody
	public String getWelcome(
	**@RequestParam**(required = false , defaultValue = "Desconocido") String name) 
	{
		return "Hola " + name;
	}

Cuando llegue una url con localhost/8080/welcome?name=algo → retornara el “hola algo”

Para que se redirija a esta url, lo mas sencillo es usar formularios, en este caso usar uno con método GET y con el input name=”nombre parámetro función”. Finalmente un botón submit.

  • Ejemplo POST

    <form action="/login" method="post">
        <label for="user">User</label>
        <input id="user" name="user" type="text">
        <label for="password">Password</label>
        <input type="text" name="password" id="password">
        <button type="submit">Enviar</button>
    </form>
    	@RequestMapping("/login")
    	@ResponseBody
    	public String login(@RequestParam String user, @RequestParam String password) {
    		return "Hola " + user + " Password: " + password;
    	}
    }

    En postman parece que se pasan los parámetros como un get, pero en el navegador se pasa internamente

    En postman parece que se pasan los parámetros como un get, pero en el navegador se pasa internamente

  • Parámetros infinitos

    Se trata de poder pasar tantos parámetros como quieras

    	**@GetMapping**("/allNames")
    	**@ResponseBody**
    	public String allNames(**@RequestParam** Map<String, String> parametros) {
    		String output = "";
    		for(String key : parametros.keySet()) {
    			output += "Clave: " + key + " Valor: " + parametros.get(key) + "<br>";
    		}
    		return output;
    	}

Después tenemos @PathVariable que se usaría para dar una URL dinámica en función de cada caso específico. Este también se pasa por parámetro.

	@GetMapping("/users/{usuario}")
	@ResponseBody
	public String userHomePage(**@PathVariable** String usuario) {
		return "Hola " + usuario;
	}

Esta anotación también puede ser usada así @PathVariable(name = id) Optional<Integer>id

https://www.notion.so/icons/info-alternate_pink.svg En este caso, indiferentemente de la URL `/users/cualquierCosa` que le pasemos, se generara una página con el “Hola cualquierCosa”, habría que crear un sistema de autentificación para que aquí no pueda ir cualquier cosa.

DataBase - JDBC

Para trabajar con ello tenemos que instalar las dependencias de → MySQL Driver, Spring Data JDBC y Spring Web

Hay que asignar la URL a la DB con user y password para que se pueda iniciar lo aplicación, sino nos dará un error.

spring.datasource.url = jdbc:mysql://localhost/nombre_db
spring.datasource.username = root
spring.datasource.password = admin
spring.datasource.driver-class-name = com.mysql.cj.jdbc.Driver

Posteriormente usaremos un objeto JdbcTemplate para interactuar con la información de la BD SQL.

https://www.notion.so/icons/info-alternate_pink.svg Para hacer debug de querries enviadas por jdbc, se puede hacer que se impriman por los loggers en el properties → `*logging.level.org.springframework.jdbc.core.JdbcTemplate = debug` Hay que tener en cuenta que si para que te haga el log y/o puedas debugear normal para ver si te devuelve datos, tienes que atacar el endpoint con el navegador.*
  1. Crear la clase con JdbcTemplate y su constructor

    @RestController
    public class AgendaController {
    	@Autowired
    	JdbcTemplate jdbcTemplate;
    	// Sin el autowired seria: JdbcTemplate jdbcTemplate = new JdbcTemplate()
    	public AgendaController(JdbcTemplate jdbcTemplate) {
    		this.jdbcTemplate = jdbcTemplate;
    	}
    }
  2. Iniciar conexión ejecutando la querry

    	@GetMapping("/allContacts")
    	public List<String> getAllContacts() {
    		final String QUERRY = "SELECT * FROM AGENDA;";
    		List<Map<String, Object>> allContacts = jdbcTemplate.queryForList(QUERRY);
    		return null;
    	}
  3. Iteración del ResultSet para convertirlo en algo printeable

    List<String> stringList = new ArrayList<String>();
    ***//Aqui pasaremos items de una lista List<Map<String, Object>> a List<String>*** 
    	for(Map<String, Object> stringItem :  allContacts) {
    		stringList.add(stringItem.toString());
    	}

    Será como un array de Strings, por lo que no puede ser accedida como un json

    Será como un array de Strings, por lo que no puede ser accedida como un json

    Ante este problema, surge la necesidad de usar un pojo para que el output sea un JSON

  4. Iteración a JSON usando POJOs

    • BD usada

      CREATE SCHEMA Agenda;
      USE Agenda;
      CREATE TABLE IF NOT EXISTS Agenda.agenda (
      	id_contacto INTEGER UNSIGNED AUTO_INCREMENT PRIMARY KEY,
          nombre VARCHAR(20),
          apellido VARCHAR(50),
          telefono INTEGER);
    • Pojo usado

      package com.example.entity;
      import java.io.Serializable;
      public class Contacto implements Serializable{
      	private Long id;
      	private String nombre;
      	private String apellidos;
      	private int telefono;
      	private static final long serialVersionUID = 1L;
      	public Long getId() {
      		return id;
      	}
      	public void setId(Long id) {
      		this.id = id;
      	}
      	public String getNombre() {
      		return nombre;
      	}
      	public void setNombre(String nombre) {
      		this.nombre = nombre;
      	}
      	public String getApellidos() {
      		return apellidos;
      	}
      	public void setApellidos(String apellidos) {
      		this.apellidos = apellidos;
      	}
      	public int getTelefono() {
      		return telefono;
      	}
      	public void setTelefono(int telefono) {
      		this.telefono = telefono;
      	}
      	public Contacto(int id, String nombre, String apellidos, int telefono) {
      		super();
      		this.id = id;
      		this.nombre = nombre;
      		this.apellidos = apellidos;
      		this.telefono = telefono;
      }}
    *// Hacer que salga como objeto por el pojo que creara Json*
    List<Contacto> **contactList** = new ArrayList<Contacto>();
    for(Map<String, Object> row :  allContacts) {	
    	Contacto contacto = new Contacto();
    	
    	//Para id
    	int id =(Long) row.get("id_contacto");
    	contacto.setId(id);
    	//Para los demas
    	contacto.setNombre((String) row.get("nombre"));
    	contacto.setApellidos((String) row.get("apellido"));
    	contacto.setTelefono((int) row.get("telefono"));
    	
    	*//Añadimos el objeto seteado a la lista creada anteriormente*
    	contactList.add(contacto);
    }

    Untitled

    Esta vez tenemos el output en JSON para poder fetchearlo correctamente en el frontEnd, y así comprobar que es lo que tenemos en la BD.

POJOS

Un Bean es un pojo pero un pojo no es un bean. Este es mas específico.

Debe implementar la interfaz Serializable, la cual dará un ID de versión para cada Bean.

En caso de que no devuelva nada se puede devolver el ResponseEntity con noContent

DataBase - JPA

  • LOMBOKSe usa para no tener que escribir getters/setters… Se hace automático

    Pasos de instalación:

    1. Añadir dependencia que se encuentra en mvnrepository en Project Lombok
    2. Una vez añadido al pom.xml, buscar la carpeta /user/.m2/lombok
    3. Ejecutar el archivo .jar con java -jar .\lombok-1.18.34.jar e instalar
    4. Reiniciar Eclipse y en Project/UpdateMavenProject → force
    https://www.notion.so/icons/info-alternate_pink.svg Ahora al crear un pojo/entidad/bean no será necesario escribir todos los getters, etc.. Simplemente con las anotaciones de Lombok se omiten.
    • Anotaciones

      @NonNull @Cleanup @Getter/@Setter @ToString @EqualsAndHashCode @NoArgsConstructor @RequiredArgsConstructor @AllArgsConstructor @Data @Value @Builder @SneakyThrows @Synchronized @Locked @With @Getter(lazy=true) @Log

    Ejemplo: Este código sustituiría al Pojo que hay mas arriba

    @Data
    public class Contacto implements Serializable{
    	private Long id;
    	private String nombre;
    	private String apellidos;
    	private int telefono;
    	private static final long serialVersionUID = 1L;
    }

Se añaden las dependencias en el pom.xml

Hay 3 pasos → Se crean ENTIDAD - REPOSITORIO - CONTROLADOR

  1. La Entidad

    Creamos lo que vendría siendo los elementos del SQL.

    **@Data
    @Entity**
    **@Table**(name = "Agenda") 
    public class ContactoJPA implements Serializable{
    	private static final long serialVersionUID = 1L;
    	**@Id
    	@GeneratedValue**(strategy = GenerationType.IDENTITY)
    	**@Column**(name = "id") //No obligatorio, solo si el atributo se llama diff.
    	public int id;
    }
  2. El repositorio → Consta de la interfaz y el servicio

    Interfaz:

    public interface ContactoRepositoryJPA 
    	extends JpaRepository<ContactoJPA, Integer>{}

    Servicio: Consta de todos los procesos CRUD prehechos

    @Repository
    public class ContactService {
    	private ContactoRepositoryJPA contactoRepositoryJPA;
    	
    	
    }
  3. Controlador

    @RestController
    public class AgendaControllerJPA {
    
    	//Se llama al service
    	final ContactService contactService;
    	//Constructor que tiene el service
    	public AgendaControllerJPA(ContactService contactService) {
    		this.contactService = contactService;
    	}
    	//TODO Hay que hacer los metodos de el controlador en JPA
    }

DTO data transfer object

Seria como hacer un pojo pero con lo que quieres de una entidad, o de mas de una entidad

modelMapper es una dependenciay con un DTO

Se hace una lista en el service de dtos que quieres u para ir añadiéndolo se hace el for in automáticamente modelMapper.map()

El nombre de atributo de el DTO tiene que llamarse exactamente igual que el de la Entidad

Relaciones

@ManyToMany → Así se saca en el Json información de una tabla y otras.

Microservicios

Resumen de todo

Primer paso de User Registration: Crear la estructura básica con Lombok y JPA

Vamos a implementar la funcionalidad de registro de usuario con la estructura típica en una aplicación Spring Boot: modelo (entidad), repositorio, servicio y controlador.


¿Para qué sirve cada capa?

  1. Capa de Modelo (Entidad):
    • Representa la tabla de la base de datos como una clase Java.
    • Contiene los atributos del usuario y sus relaciones con otras tablas.
    • Usaremos JPA para mapear automáticamente la entidad a la base de datos.
    • Con Lombok, simplificamos el código eliminando getters, setters y constructores manuales.
  2. Capa de Repositorio:
    • Es la interfaz que conecta con la base de datos para operaciones CRUD (crear, leer, actualizar, eliminar).
    • JPA simplifica las consultas comunes y permite personalizar otras más complejas.
    • Con esta capa, no necesitamos escribir código SQL directamente.
  3. Capa de Servicio:
    • Contiene la lógica de negocio. Aquí validamos los datos, verificamos condiciones (como que el correo no exista) y gestionamos tareas complejas antes de interactuar con el repositorio.
  4. Capa de Controlador:
    • Expone los endpoints que el cliente (frontend, Postman, etc.) utiliza para interactuar con la aplicación.
    • Recibe las solicitudes HTTP, llama al servicio y devuelve la respuesta al cliente.

Ventajas de usar Lombok y JPA

  1. Lombok:

    • Reduce código repetitivo (getters, setters, constructores, etc.).

    • Mejora la legibilidad del código.

    • Ejemplo:Esto genera automáticamente los métodos getName(), setName(String), y un constructor vacío.

      java
      Copiar código
      @Getter
      @Setter
      @NoArgsConstructor
      public class User {
          private String name;
          private String email;
      }
  2. JPA:

    • Se encarga de mapear las entidades a tablas en la base de datos.

    • Permite realizar operaciones CRUD sin escribir SQL.

    • Ejemplo:Esto crea automáticamente la tabla User en la base de datos con columnas id, name, y email.

      java
      Copiar código
      @Entity
      public class User {
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          private Long id;
          private String name;
          private String email;
      }

Implementación del primer paso

1. Modelo (Entidad)

  1. Crea la clase User en el paquete Entities:

    java
    Copiar código
    package com.hackathon.finservice.Entities;
    
    import jakarta.persistence.*;
    import lombok.*;
    
    @Entity
    @Table(name = "users") // Nombre de la tabla en la base de datos
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY) // Genera ID automáticamente
        private Long id;
    
        @Column(nullable = false) // Campo obligatorio
        private String name;
    
        @Column(nullable = false, unique = true) // Debe ser único
        private String email;
    
        @Column(nullable = false) // Se almacenará la contraseña hasheada
        private String password;
    
        @Column(nullable = false, unique = true) // Debe ser único
        private String accountNumber;
    
        @Column(nullable = false)
        private String accountType = "Main"; // Tipo de cuenta por defecto
    }

¿Qué hace JPA aquí?

  • @Entity: Marca la clase como una tabla en la base de datos.
  • @Table(name = "users"): Opcionalmente da un nombre específico a la tabla.
  • @Id y @GeneratedValue: Declaran la columna id como clave primaria y generan valores automáticamente.
  • @Column: Configura los atributos como columnas, marcando restricciones como nullable o unique.

¿Qué hace Lombok aquí?

  • @Getter y @Setter: Generan automáticamente los getters y setters.

  • @NoArgsConstructor y @AllArgsConstructor: Generan constructores vacío y con todos los argumentos.

  • @Builder: Permite crear objetos con un estilo fluido:

    java
    Copiar código
    User user = User.builder()
                    .name("Nuwe Test")
                    .email("nuwe@nuwe.com")
                    .password("hashedPassword")
                    .accountNumber("19b332")
                    .build();

2. Repositorio

  1. Crea la interfaz UserRepository en el paquete Repositories:

    java
    Copiar código
    package com.hackathon.finservice.Repositories;
    
    import com.hackathon.finservice.Entities.User;
    import org.springframework.data.jpa.repository.JpaRepository;
    import java.util.Optional;
    
    public interface UserRepository extends JpaRepository<User, Long> {
        Optional<User> findByEmail(String email); // Busca usuario por email
    }

¿Qué hace JPA aquí?

  • JpaRepository: Proporciona métodos como save, findById, findAll, y delete automáticamente.
  • El método personalizado findByEmail usa la convención de nombres de JPA para generar automáticamente la consulta.

3. Servicio

  1. Crea la clase UserService en el paquete Service:

    java
    Copiar código
    package com.hackathon.finservice.Service;
    
    import com.hackathon.finservice.Entities.User;
    import com.hackathon.finservice.Repositories.UserRepository;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    import java.util.UUID;
    
    @Service
    public class UserService {
        @Autowired
        private UserRepository userRepository;
    
        public User registerUser(User user) {
            // Validar que el correo no exista
            if (userRepository.findByEmail(user.getEmail()).isPresent()) {
                throw new IllegalArgumentException("El correo ya está registrado");
            }
    
            // Generar número de cuenta único
            user.setAccountNumber(UUID.randomUUID().toString().substring(0, 6));
    
            // Guardar usuario
            return userRepository.save(user);
        }
    }

¿Qué hace el servicio?

  • Contiene la lógica de negocio: Validación y generación del número de cuenta.
  • Interactúa con el repositorio para guardar al usuario.

4. Controlador

  1. Crea la clase UserController en el paquete Controllers:

    java
    Copiar código
    package com.hackathon.finservice.Controllers;
    
    import com.hackathon.finservice.Entities.User;
    import com.hackathon.finservice.Service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/api/users")
    public class UserController {
        @Autowired
        private UserService userService;
    
        @PostMapping("/register")
        public ResponseEntity<?> registerUser(@RequestBody User user) {
            User registeredUser = userService.registerUser(user);
            return ResponseEntity.ok(registeredUser);
        }
    }

¿Qué hace el controlador?

  • Define el endpoint /api/users/register.
  • Recibe el cuerpo de la solicitud HTTP (User) y llama al servicio para registrar al usuario.
  • Devuelve la respuesta al cliente.

Prueba final

  1. Ejecuta tu aplicación Spring Boot.

  2. Usa Postman o curl para probar el endpoint:

    bash
    Copiar código
    curl -X POST http://localhost:3000/api/users/register \
         -H "Content-Type: application/json" \
         -d '{"name":"Nuwe Test","email":"nuwe@nuwe.com","password":"NuweTest1$"}'
  3. Verifica que el usuario se registre correctamente en la base de datos.


Con esto tienes listo el registro básico de usuarios. ¿Alguna duda o necesitas ayuda con la validación o el hashing de contraseñas? 😊

HACKATHON CaixaBank

1- Dockerizar la aplicación

Enunciado: Hacer que esta estructura de app se ejecute

  • Estructura

    caixabank-backend-java-bankingapp/
    ├── docker-compose.yml
    ├── Dockerfile
    ├── mvnw
    ├── mvnw.cmd
    ├── pom.xml
    ├── [README.md](http://readme.md/)
    └── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── hackathon
    │   │           └── finservice
    │   │               ├── Config
    │   │               │   └── CorsConfig.java
    │   │               ├── Controllers
    │   │               │   └── HealthCheckController.java
    │   │               ├── DTO
    │   │               ├── Entities
    │   │               │   ├── Account.java
    │   │               │   ├── Token.java
    │   │               │   ├── Transaction.java
    │   │               │   ├── TransactionStatus.java
    │   │               │   ├── TransactionType.java
    │   │               │   └── User.java
    │   │               ├── Exception
    │   │               ├── FinserviceApplication.java
    │   │               ├── Repositories
    │   │               ├── Security
    │   │               ├── Service
    │   │               └── Util
    │   │                   └── JsonUtil.java
    │   └── resources
    │       └── application.properties
    └── test
    └── java
    └── com
    └── hackathon
    └── finservice
  • Docker-compose

    version: '3.8'
    
    services:
      app:
        build:
          context: .
          dockerfile: Dockerfile
        ports:
          - "3000:3000"
        depends_on:
          - mysql
        networks:
          - finservice_network
        restart: always
    
      mysql:
        image: mysql:8.0
        environment:
          MYSQL_DATABASE: bankingapp             
          MYSQL_PASSWORD: root                   
          MYSQL_ROOT_PASSWORD: root              
        ports:
          - "3306:3306"                          
        networks:
          - finservice_network
        restart: always
    
    networks:
      finservice_network:
        driver: bridge
    
  • DockerFile

    # Usa una imagen base de Maven para compilar el proyecto
    FROM maven:3.8.5-openjdk-17 AS build
    
    # Establece el directorio de trabajo dentro del contenedor
    WORKDIR /app
    
    # Copia el archivo pom.xml y descarga las dependencias (mejora la caché de Docker)
    COPY pom.xml .
    RUN mvn dependency:go-offline -B
    
    # Copia el resto de los archivos del proyecto
    COPY src ./src
    
    # Construye el proyecto
    RUN mvn clean package -DskipTests
    
    # Usa una imagen base más ligera para ejecutar la aplicación
    FROM openjdk:17-jdk-slim
    
    # Establece el directorio de trabajo dentro del contenedor
    WORKDIR /app
    
    # Copia el archivo .jar generado en la etapa anterior
    COPY --from=build /app/target/*.jar app.jar
    
    # Expone el puerto 3000
    EXPOSE 3000
    
    # Comando para ejecutar la aplicación
    ENTRYPOINT ["java", "-jar", "app.jar"]
    

2- User backend management

En este caso se usará con JPA y LOMBOK para no tener que escribir el sql a mano desde la app de java

  • Output Chatgpt bastante bueno para este caso

    Primer paso de User Registration: Crear la estructura básica con Lombok y JPA

    Vamos a implementar la funcionalidad de registro de usuario con la estructura típica en una aplicación Spring Boot: modelo (entidad), repositorio, servicio y controlador.


    ¿Para qué sirve cada capa?

    1. Capa de Modelo (Entidad):
      • Representa la tabla de la base de datos como una clase Java.
      • Contiene los atributos del usuario y sus relaciones con otras tablas.
      • Usaremos JPA para mapear automáticamente la entidad a la base de datos.
      • Con Lombok, simplificamos el código eliminando getters, setters y constructores manuales.
    2. Capa de Repositorio:
      • Es la interfaz que conecta con la base de datos para operaciones CRUD (crear, leer, actualizar, eliminar).
      • JPA simplifica las consultas comunes y permite personalizar otras más complejas.
      • Con esta capa, no necesitamos escribir código SQL directamente.
    3. Capa de Servicio:
      • Contiene la lógica de negocio. Aquí validamos los datos, verificamos condiciones (como que el correo no exista) y gestionamos tareas complejas antes de interactuar con el repositorio.
    4. Capa de Controlador:
      • Expone los endpoints que el cliente (frontend, Postman, etc.) utiliza para interactuar con la aplicación.
      • Recibe las solicitudes HTTP, llama al servicio y devuelve la respuesta al cliente.

    Ventajas de usar Lombok y JPA

    1. Lombok:

      • Reduce código repetitivo (getters, setters, constructores, etc.).

      • Mejora la legibilidad del código.

      • Ejemplo:Esto genera automáticamente los métodos getName(), setName(String), y un constructor vacío.

        java
        Copiar código
        @Getter
        @Setter
        @NoArgsConstructor
        public class User {
            private String name;
            private String email;
        }
    2. JPA:

      • Se encarga de mapear las entidades a tablas en la base de datos.

      • Permite realizar operaciones CRUD sin escribir SQL.

      • Ejemplo:Esto crea automáticamente la tabla User en la base de datos con columnas id, name, y email.

        java
        Copiar código
        @Entity
        public class User {
            @Id
            @GeneratedValue(strategy = GenerationType.IDENTITY)
            private Long id;
            private String name;
            private String email;
        }

    Implementación del primer paso

    1. Modelo (Entidad)

    1. Crea la clase User en el paquete Entities:

      java
      Copiar código
      package com.hackathon.finservice.Entities;
      
      import jakarta.persistence.*;
      import lombok.*;
      
      @Entity
      @Table(name = "users") // Nombre de la tabla en la base de datos
      @Getter
      @Setter
      @NoArgsConstructor
      @AllArgsConstructor
      @Builder
      public class User {
      
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY) // Genera ID automáticamente
          private Long id;
      
          @Column(nullable = false) // Campo obligatorio
          private String name;
      
          @Column(nullable = false, unique = true) // Debe ser único
          private String email;
      
          @Column(nullable = false) // Se almacenará la contraseña hasheada
          private String password;
      
          @Column(nullable = false, unique = true) // Debe ser único
          private String accountNumber;
      
          @Column(nullable = false)
          private String accountType = "Main"; // Tipo de cuenta por defecto
      }

    ¿Qué hace JPA aquí?

    • @Entity: Marca la clase como una tabla en la base de datos.
    • @Table(name = "users"): Opcionalmente da un nombre específico a la tabla.
    • @Id y @GeneratedValue: Declaran la columna id como clave primaria y generan valores automáticamente.
    • @Column: Configura los atributos como columnas, marcando restricciones como nullable o unique.

    ¿Qué hace Lombok aquí?

    • @Getter y @Setter: Generan automáticamente los getters y setters.

    • @NoArgsConstructor y @AllArgsConstructor: Generan constructores vacío y con todos los argumentos.

    • @Builder: Permite crear objetos con un estilo fluido:

      java
      Copiar código
      User user = User.builder()
                      .name("Nuwe Test")
                      .email("nuwe@nuwe.com")
                      .password("hashedPassword")
                      .accountNumber("19b332")
                      .build();

    2. Repositorio

    1. Crea la interfaz UserRepository en el paquete Repositories:

      java
      Copiar código
      package com.hackathon.finservice.Repositories;
      
      import com.hackathon.finservice.Entities.User;
      import org.springframework.data.jpa.repository.JpaRepository;
      import java.util.Optional;
      
      public interface UserRepository extends JpaRepository<User, Long> {
          Optional<User> findByEmail(String email); // Busca usuario por email
      }

    ¿Qué hace JPA aquí?

    • JpaRepository: Proporciona métodos como save, findById, findAll, y delete automáticamente.
    • El método personalizado findByEmail usa la convención de nombres de JPA para generar automáticamente la consulta.

    3. Servicio

    1. Crea la clase UserService en el paquete Service:

      java
      Copiar código
      package com.hackathon.finservice.Service;
      
      import com.hackathon.finservice.Entities.User;
      import com.hackathon.finservice.Repositories.UserRepository;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import java.util.UUID;
      
      @Service
      public class UserService {
          @Autowired
          private UserRepository userRepository;
      
          public User registerUser(User user) {
              // Validar que el correo no exista
              if (userRepository.findByEmail(user.getEmail()).isPresent()) {
                  throw new IllegalArgumentException("El correo ya está registrado");
              }
      
              // Generar número de cuenta único
              user.setAccountNumber(UUID.randomUUID().toString().substring(0, 6));
      
              // Guardar usuario
              return userRepository.save(user);
          }
      }

    ¿Qué hace el servicio?

    • Contiene la lógica de negocio: Validación y generación del número de cuenta.
    • Interactúa con el repositorio para guardar al usuario.

    4. Controlador

    1. Crea la clase UserController en el paquete Controllers:

      java
      Copiar código
      package com.hackathon.finservice.Controllers;
      
      import com.hackathon.finservice.Entities.User;
      import com.hackathon.finservice.Service.UserService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.http.ResponseEntity;
      import org.springframework.web.bind.annotation.*;
      
      @RestController
      @RequestMapping("/api/users")
      public class UserController {
          @Autowired
          private UserService userService;
      
          @PostMapping("/register")
          public ResponseEntity<?> registerUser(@RequestBody User user) {
              User registeredUser = userService.registerUser(user);
              return ResponseEntity.ok(registeredUser);
          }
      }

    ¿Qué hace el controlador?

    • Define el endpoint /api/users/register.
    • Recibe el cuerpo de la solicitud HTTP (User) y llama al servicio para registrar al usuario.
    • Devuelve la respuesta al cliente.

    Prueba final

    1. Ejecuta tu aplicación Spring Boot.

    2. Usa Postman o curl para probar el endpoint:

      bash
      Copiar código
      curl -X POST http://localhost:3000/api/users/register \
           -H "Content-Type: application/json" \
           -d '{"name":"Nuwe Test","email":"nuwe@nuwe.com","password":"NuweTest1$"}'
    3. Verifica que el usuario se registre correctamente en la base de datos.


    Con esto tienes listo el registro básico de usuarios. ¿Alguna duda o necesitas ayuda con la validación o el hashing de contraseñas? 😊

  • DEFINICIONES

    image.png

    Entidad/Modelo

    Sirve para crear el objeto literal de la tabla sql en java. Tendra atributos de la tabla y conexiones con otras.

    Lombok → Evita codigo repetitivo. Se generan automaticamente @Getter @Setter de cada campo. Tambien genera el constructor

    JPA → 2 funciones

    1. Analiza el codigo del objeto creado y lo crea en la BD automaticamente. No hace falta ir creando la bd a mano en el MySql
    2. Crea automaticamente todo el CRUD para no tener que ir escribiendo sql en tu back.

    Repositorio

    Conecta el CRUD con la BD directamente

    Servicio

    Es la lógica de negocios, pondremos las verificaciones extra que querramos etc etc

    Controlador

    Define los endpoints. Reciber solicitudes http y consulta al servicio.


  • IMPLEMENTACIÓN Entidad - Repositorio - Servicio - Controlador

    Se nos pide que usuario pueda devolver json en el que el mas completo tenga los campos: name, email, password, accountNumber, accountType.

    Modelo/Entidad

    (Esto a continuación son anotaciones de JPA)

    Clase/tabla

    JPA

    @Entity → Se marca para que la clase se entienda como una tabla de BD

    @Table(name =””) → Si la tabla se tiene que llamar diferente a la de la clase, se especifica

    Lombok

    @Getter @Setter @AllArgsConstructor @NoArgsConstructor

    @Builder → Permite crear objetos con estilo fluido (Syntaxis)

    • Función de un Builder

      Al haber esa concatenación de . y tabulaciones el codigo queda mas claro

      // Constructor largo
      new Persona("Juan", 30, "Calle Principal 123");
      
      // Con Builder
      Persona.builder()
             .nombre("Juan")
             .edad(30)
             .direccion("Calle Principal 123")
             .build();

    Id→ Se añade @Id y @GeneratedValue para que se establezca la clave primaria al generar la tabla y ademas los valores se generen automaticamente.

    Atributos → Marcaremos que son columnas y por parametros especificamos nullable or not. Se añadem @Column(nullable = false, unique = true)

    • Codigo completo Entidad

      package com.hackathon.finservice.Entities;
      
      import jakarta.persistence.*;
      import lombok.*;
      
      @Entity
      @Table(name = "users") // Nombre de la tabla en la base de datos
      @Getter
      @Setter
      @NoArgsConstructor
      @AllArgsConstructor
      @Builder
      public class User {
      
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY) // Genera ID automáticamente
          private Long id;
      
          @Column(nullable = false) // Campo obligatorio
          private String name;
      
          @Column(nullable = false, unique = true) // Debe ser único
          private String email;
      
          @Column(nullable = false) // Se almacenará la contraseña hasheada
          private String password;
      
          @Column(nullable = false, unique = true) // Debe ser único
          private String accountNumber;
      
          @Column(nullable = false)
          private String accountType = "Main"; // Tipo de cuenta por defecto
      }

    Repositorio

    Importamos el Modelo/Entidad

    Es la clase que tiene los metodos del CRUD y los hereda de JpaRepository

    • Metodos Default
      • save(T entity): Guarda o actualiza una entidad en la base de datos.
      • findById(ID id): Busca una entidad por su clave primaria.
      • findAll(): Recupera todas las entidades de la base de datos.
      • delete(T entity): Elimina una entidad específica.
      • deleteById(ID id): Elimina una entidad usando su clave primaria.
    • Metodos Personalizados *Optional<User>* **findByEmail***(String email);*
      • Tambien se pueden generar metodos personalizados añadiendolos a la clase
      • Si se sigue la convencion findBy + atributo, no hace falta definir el metodo, ya que JPA lo crea automaticamente para que haga → *SELECT * FROM user WHERE email = ?;*
      • Optional es para poder gestionar los valores nulos, que nos devolveran Optional.empty() que es mas seguro
    • En este caso, como el JpaRepository está parametrizado con <User, Long>, significa:
      • User: Es la entidad que este repositorio gestionará.

      • Long: Es el tipo de la clave primaria (ID) de la entidad User.

      • Codigo Completo Repositorio

        package com.hackathon.finservice.Repositories;
        
        import com.hackathon.finservice.Entities.User;
        import org.springframework.data.jpa.repository.JpaRepository;
        import java.util.Optional;
        
        public interface UserRepository extends JpaRepository<User, Long> {
            Optional<User> findByEmail(String email); // Busca usuario por email
        }

    Servicio

    Importa de → Entidad/Modelo y repositorio

    Tiene

    Atributo de repositorio inyectado

    Metodos para gestionar logica

    Es la capa de lógica de negocio. Aqui comprobaremos que el correo sea correcto, que el UUID se genera bien….

    • Requsitos para este caso → No puede haber campos vacios, email valido y que no exista en la bbdd.

    • Anotaciones

      • @Service → Indica que tiene la logica, y se gestiona como un bean por Spring
      • @Autowired → Permite a Spring inyectar el UserRepository como dependencia
    • Libreria extra UUID → java.utils.UUID

      • Permite generar UUIDs
    • Codigo Completo Service

      package com.hackathon.finservice.Service;
      
      import com.hackathon.finservice.Entities.User;
      import com.hackathon.finservice.Repositories.UserRepository;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      
      import java.util.UUID;
      
      @Service
      public class UserService {
          @Autowired
          private UserRepository userRepository;
      
          public User registerUser(User user) {
              // Validar que el correo no exista
              if (userRepository.findByEmail(user.getEmail()).isPresent()) {
                  throw new IllegalArgumentException("El correo ya está registrado");
              }
      
              // Generar número de cuenta único
              user.setAccountNumber(UUID.randomUUID().toString().substring(0, 6));
      
              // Guardar usuario
              return userRepository.save(user);
          }
      }

    Controlador

    Importa de → Servicio y Entidad

    Usa ResponseEntity para usar los codigos predefinidos que se devuelven como html.

    @RestController → Asigna la clase como controlador MVC

    → Maneja Http y devuelve Json

    → Controller + ResponseBody

    @RequestMapping → Define la ruta base

    1. Se crea un UserService atributo con @Autowired
    2. Metodo
      1. Devuelve un ResponseEntity<> → 200 OK..
      2. Parametro RequestBody → User user, un json del usuario
      3. Se llama al servicio y aplica la logica, devuelve un objeto user si esta todo ok
    @PostMapping("/register")
    public ResponseEntity<?> registerUser(@RequestBody User user) {
        User registeredUser = userService.registerUser(user);
        return ResponseEntity.ok(registeredUser);
    } 
    == return new ResponseEntity<>(registeredUser, HttpStatus.OK);
    https://www.notion.so/icons/info-alternate_orange.svg

    Si el usuario no acepta la lógica de negocio y no se registra

    Entonces en el servicio devolviamos una excepcion, que deberemos controlar en el controlador tal que:

        try {
            User registeredUser = userService.registerUser(user);
            return ResponseEntity.ok(registeredUser);
        } catch (IllegalArgumentException e) {
            return ResponseEntity.badRequest().body(e.getMessage()); 
            // Devuelve un 400 con el mensaje de error.
        }
    • Manejo Centralizado Errores

      Se puede crear una clase reutilizable para los errores que siempre muestre el mensaje del error.

      @RestControllerAdvice
      public class GlobalExceptionHandler {
      
          @ExceptionHandler(IllegalArgumentException.class)
          public ResponseEntity<?> handleIllegalArgumentException(IllegalArgumentException e) {
              return ResponseEntity.badRequest().body(e.getMessage());
          }
      }
      //DEVUELVE:
      {
          "status": 400,
          "error": "Bad Request",
          "message": "El correo ya está registrado",
          "timestamp": "2024-11-23T10:00:00Z"
      }
    • Codigo completo Controller

      package com.hackathon.finservice.Controllers;
      
      import com.hackathon.finservice.Entities.User;
      import com.hackathon.finservice.Service.UserService;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.http.ResponseEntity;
      import org.springframework.web.bind.annotation.*;
      
      @RestController
      @RequestMapping("/api/users")
      public class UserController {
          @Autowired
          private UserService userService;
      
          @PostMapping("/register")
          public ResponseEntity<?> registerUser(@RequestBody User user) {
              User registeredUser = userService.registerUser(user);
              return ResponseEntity.ok(registeredUser);
          }
      }
  • Logica de negocio

    https://www.notion.so/icons/info-alternate_orange.svg

    Para sacar un usuario a partir de un id o de algun elemento.

    Optional<User> optionalUser = userRepository.findByEmail(email);

    • Se te guarda automaticamente en la variable Optional un User, sino hay nada, se comprueba con un .isEmpty()
    • Si esta → User user = optionalUser.get() y ahi si lo guardas bene
  • DTO

    Sirven para devolver un json personalizado, con mas campos de los que habitualmente se tienen

    package com.hackathon.finservice.Dto;
    
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    
    @Data // Genera getters, setters, toString, equals, y hashCode
    @Builder // Genera un patrón de builder para crear objetos
    @AllArgsConstructor // Genera un constructor con todos los argumentos
    public class UserResponse {
        private String name;
        private String email;
        private String accountNumber;
        private String accountType;
        private String hashedPassword;
    }
  • JWT

    • CREAR TOKEN

      • subject: Identificador único del usuario (en este caso, accountNumber).
      • issuedAt: Fecha de emisión del token.
      • expiration: Fecha de expiración del token.
      • signWith: Firma el token con el algoritmo HS512 y la clave secreta.
      @Component
      public class JwtUtil {
      
          private final String SECRET_KEY = "my_secret_key"; // Cambiar por una clave más segura
      
          public String generateToken(String subject) {
              return Jwts.builder()
                      .setSubject(subject) // El identificador principal (número de cuenta)
                      .setIssuedAt(new Date()) // Fecha de emisión
                      .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10 horas
                      .signWith(SignatureAlgorithm.HS512, SECRET_KEY) // Algoritmo y clave secreta
                      .compact();
          }
      }

      En el UserService si todo esta correcto, en la funcion de login() pondriamos return jwtUtil.generateToken(user.getAccountNumber());

      Esto nos devolvera un token único si el login es 200 OK.

    • USAR EL TOKEN postLogin

      Ahora hay que validar el Token para que el usuario pueda obtener la información

      1. En SecurityConfig → Añadir sessionManagement y el filtro que crearemos

        • Codigo de SecurityConfig

          @Configuration
          public class SecurityConfig {
          
          @Bean
          public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationFilter jwtAuthenticationFilter) throws Exception {
              http
                  .authorizeHttpRequests(authorize -> authorize
                      .requestMatchers("/api/users/login", "/api/users/register", "/health").permitAll()
                      .anyRequest().authenticated()
                  )
                  **.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class) // Agrega el filtro aquí**
                  .csrf().disable()
                  **.sessionManagement().disable(); // Sin estado, ideal para JWT**
          
              return http.build();
          	}
          }
      2. Crear archivo JwtAuthenticationFilter en el paquete/carpeta Security

        • Se marca como Component, significa que se gestiona como bean y se podra Autowirear cuando se declare. Extends from OncePerRequestFilter que solo permite una validacion por request http

        • Usamos jwtUtil porque queremos extraer el AccountNumber UUID del token

        • Obtiene el token de el header http “Authorization” e inicializa jwt y accountNumber

          final String authHeader = request.getHeader("Authorization");
          String jwt = null;
          String accountNumber = null;
        • Verifica si el encabezado tiene un token

          if (authHeader != null && authHeader.startsWith("Bearer ")) {
          jwt = authHeader.substring(7);
          accountNumber = jwtUtil.extractSubject(jwt); // Extraer el accountNumber del token
          }
        • Valida el token y gestiona la autentificación

        • Codigo completo Auth

          package com.hackathon.finservice.Security;
          
          @Component
          public class JwtAuthenticationFilter extends OncePerRequestFilter {
          
              @Autowired
              private JwtUtil jwtUtil;
          
              @Override
              protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
                      throws ServletException, IOException {
          
                  final String authHeader = request.getHeader("Authorization");
                  String jwt = null;
                  String accountNumber = null;
          
                  // Verificar si el encabezado contiene el token
                  if (authHeader != null && authHeader.startsWith("Bearer ")) {
                      jwt = authHeader.substring(7);
                      accountNumber = jwtUtil.extractSubject(jwt); // Extraer el accountNumber del token
                  }
          
                  // Si el token es válido, configura la autenticación
                  if (accountNumber != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                      if (jwtUtil.validateToken(jwt)) {
                          UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                                  accountNumber, null, null);
                          authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                          SecurityContextHolder.getContext().setAuthentication(authentication);
                      }
                  }
          
                  chain.doFilter(request, response);
              }
          }
      3. Actualizar JwtUtil con los metodos extractSubject y validateToken

        Hemos usado estos metodos en el Auth para gestionar la info del token, así que hay que implementarlos

        • extractSubject → Viene primero de extractAll

          // Método interno para extraer todos los claims del token
          private Claims extractAllClaims(String token) {
              return Jwts.parser()
                      .setSigningKey(SECRET_KEY) 
                 // Configura la clave secreta para validar la firma
                      .parseClaimsJws(token) // Analiza el token JWT
                      .getBody(); // Extrae los claims del token
          }
          
              // Extrae el sujeto del token (el número de cuenta)
          public String extractSubject(String token) {
              return extractAllClaims(token).getSubject();
          }
        • Validate

          // Valida el token verificando su firma y si ha expirado
          public boolean validateToken(String token) {
              try {
                  extractAllClaims(token); // Intenta extraer los claims para validar
                  return true; // Si no lanza una excepción, el token es válido
              } catch (ExpiredJwtException | MalformedJwtException | SignatureException |
                       UnsupportedJwtException | IllegalArgumentException e) {
                  System.out.println("Token inválido: " + e.getMessage());
                  return false;
              }
          }
      4. Crear el controlador para esta acción

      Logica de controlador

      • Codigo

        package com.hackathon.finservice.Controllers;
        
        @RestController
        @RequestMapping("/api/dashboard")
        public class DashboardController {
        
            @Autowired
            private UserRepository userRepository;
        
            /**
             * Endpoint para obtener información del usuario principal
             */
            @GetMapping("/user")
            public ResponseEntity<?> getUserInfo() {
                // Obtener el accountNumber del token JWT
                String accountNumber = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        
                // Recuperar el usuario desde la base de datos
                User user = userRepository.findByAccountNumber(accountNumber)
                        .orElseThrow(() -> new IllegalArgumentException("User not found for account number: " + accountNumber));
        
                // Crear respuesta personalizada (opcional, si no quieres enviar la entidad completa)
                return ResponseEntity.ok(Map.of(
                        "name", user.getName(),
                        "email", user.getEmail(),
                        "accountNumber", user.getAccountNumber(),
                        "accountType", "Main", // Suponemos que siempre se devolverá "Main" para el tipo principal
                        "hashedPassword", user.getPassword()
                ));
            }
        }

      Finalmente es PostMan, usar el Bearer Token y introducir el token sin nada mas

  • Entidad - Repositorio - Controlador SIN SERVICIO

    No siempre es necesario logica de negocio, de manera que nos podemos evitar esa clase. Pero lo correcto seria siempre crearla para encapsular el codigo.

    Pondre un ejemplo simple para entender el funcionamiento de estos 3 juntos

    Modelo

    @Entity
    @Table(name = "accounts")
    @Getter
    @Setter
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public class Account{
    
        @Id
        @GeneratedValue
        private long id;
    
        @Column(nullable = false, unique = true)
        private String accountNumber;
    
        @Column(nullable = false)
        private Double balance;
    
        @Column(nullable = false)
        private String accountType = "Main";
    }

    Repositorio

    public interface AccountRepository extends JpaRepository<Account, Long> {
        Optional<Account> findByAccountNumber(String accountNumber);
    }

    Controlador

    @RestController
    @RequestMapping("api/dashboard")
    public class DashboardController {
        @Autowired
        private AccountRepository accountRepository;
    
    // Este get Mapping es estatico, pero lo dinamico viene a partir 
    //				de token asi que no se maneja por url
    @GetMapping("/account")
    public ResponseEntity<?> getAccountInfo(){
    
        // Obtener el accountNumber del token JWT
        String accountNumber = (String) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    
        Account account = accountRepository.findByAccountNumber(accountNumber)
                .orElseThrow(() -> new IllegalArgumentException("User not found for account number: " + accountNumber));
    
        // Crear respuesta personalizada
        return ResponseEntity.ok(Map.of(
                "accountNumber", account.getAccountNumber(),
                "balance", account.getBalance(),
                "accountType", account.getAccountType()
        ));
    }

TODO → Refactorizar entities para que contengan → Usuario una lista de accounts

→ Accounts un usuario

Despues arreglar los controllers y servicios para que se mapeen correctamente

Generar DTOs para las respuestas

HACKATHHON INDITEX

JPA

Funciones útiles de JPA para interaccionar con BD

save() → Crea una nueva fila en SQL de la entidad que le pasemos si esa entidad no tiene asignado un id. Si lo tiene asignado, hara un update.

@returns el id del creado.

REACT

  • npm node_modules

    Se puede trabajar a través de CDN(deprecated)

    El npm install que usaremos será en appData/Roaming/node_modules, que es como mas global, o sino se puede meter en la propia carpeta del proyecto pero es peor realmente.

    Si te lo bajaras de git, habría que hacer un npm i ya que no tendrás node modules de React.

    Una cosa es el node modules para crear el proyecto y el otro para que funcione la app

  • Set up vanilla

    npm i -g create-react-app Seria para guardar el node modules en global y en la carpeta que estés pues iniciar el proyecto.

    npx create-react-app nombre-proyecto npx seria para cogerlo de internet o algo así y después un npm start

Descargar EsLint (plugin VSC) para que de mas info.

  • Componente simple

    const React = require("react");
    class Comp extends React.Component{
        render(){
            return React.createElement(
                "div", null, "Texto dentro de el componente"
            );
        }
    }
    export default Comp;
    const Comp = () => {
        return (
        <div>Componente en JSX</div>
    )}

Babel → transpilador interno de jsx

React Fragment para pasar mas de un elemento o un componente, se puede usar directamente sin nada <> </>

Opciones css en JSX

Para js es un bloque de escape {} para css serian 2.

Css en el mismo componente con el escape de llaves →

  • Para el css puedes usar una constante que usaras luego en la función de el componente, todo en el mismo archivo.

  • Puedes hacer archivos de estilos externos que importaras después como si fuera un objeto.

  • O también usar module.nombreArchivo, entonces lo importas como styles, y luego aplicas styles al style y solo cogerá el que tenga el mismo nombre

    //En el archivo css tienes:
    .title {background-color: 'red';}
    
    //En el jsx del componente
    import styles from module.NombreArchivo.css
    <div className= {styles.title}>
    	Elementos
    </div>

Props

→ Se trata de pasar por parámetros a los componentes información. Padre pasa props a el hijo y hijo pasa eventos al padre.

Hooks y Eventos

  • Crear Proyecto con Vite

    npm i -g vite create-vite

    npm create vite@latest

    npm install y npm run dev

React Router → Hay que instalarlo

npm i react-router-dom bundlephovia(web para ver la composición de los paquetes)

import BrowserRouter from 'react-router-dom'
<BrowserRouter>
	<App />
</BrowserRouter>
import {Routes, Route, Link, UseParams} from 'react-router-dom'
function App() {
	return(
		<>
			<Routes>
				<**Route** path="/ruta1" element = {"<Componente />"}/> *o pagina*
			</Routes>
		</>
		)
	}

Para que refresque solamente lo que actualizas, se trabaja con Link en vez de

<Link to=”/pagina”> Go to pagina</Link>

Single page application SPA/MPA

useParams → para coger el nombre de la ruta

React toastify, sweetalert, sonner , mui