Add TypeORM

master
Araozu 2023-05-07 12:44:02 -05:00
parent 0d412b624d
commit b1ea575683
20 changed files with 10762 additions and 111 deletions

View File

@ -27,6 +27,7 @@
"@nestjs/platform-express": "^9.0.0", "@nestjs/platform-express": "^9.0.0",
"@nestjs/serve-static": "^3.0.1", "@nestjs/serve-static": "^3.0.1",
"@nestjs/typeorm": "^9.0.1", "@nestjs/typeorm": "^9.0.1",
"axios": "^1.4.0",
"mysql2": "^3.2.4", "mysql2": "^3.2.4",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0", "rxjs": "^7.2.0",

View File

@ -17,6 +17,7 @@ specifiers:
'@typescript-eslint/eslint-plugin': ^5.0.0 '@typescript-eslint/eslint-plugin': ^5.0.0
'@typescript-eslint/parser': ^5.0.0 '@typescript-eslint/parser': ^5.0.0
autoprefixer: ^10.4.14 autoprefixer: ^10.4.14
axios: ^1.4.0
esbuild: ^0.17.18 esbuild: ^0.17.18
esbuild-plugin-solid: ^0.5.0 esbuild-plugin-solid: ^0.5.0
eslint: ^8.0.1 eslint: ^8.0.1
@ -48,6 +49,7 @@ dependencies:
'@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne
'@nestjs/serve-static': 3.0.1_gqkob5jndcfeb3wjw72wbdwyne '@nestjs/serve-static': 3.0.1_gqkob5jndcfeb3wjw72wbdwyne
'@nestjs/typeorm': 9.0.1_5jgqalkub5zb475ngukfuhbndu '@nestjs/typeorm': 9.0.1_5jgqalkub5zb475ngukfuhbndu
axios: 1.4.0
mysql2: 3.3.0 mysql2: 3.3.0
reflect-metadata: 0.1.13 reflect-metadata: 0.1.13
rxjs: 7.8.1 rxjs: 7.8.1
@ -2183,7 +2185,6 @@ packages:
/asynckit/0.4.0: /asynckit/0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: true
/atomic-sleep/1.0.0: /atomic-sleep/1.0.0:
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
@ -2216,6 +2217,16 @@ packages:
- supports-color - supports-color
dev: true dev: true
/axios/1.4.0:
resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
dependencies:
follow-redirects: 1.15.2
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
dev: false
/babel-jest/29.5.0_@babel+core@7.21.8: /babel-jest/29.5.0_@babel+core@7.21.8:
resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==} resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -2617,7 +2628,6 @@ packages:
engines: {node: '>= 0.8'} engines: {node: '>= 0.8'}
dependencies: dependencies:
delayed-stream: 1.0.0 delayed-stream: 1.0.0
dev: true
/commander/2.20.3: /commander/2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@ -2762,7 +2772,6 @@ packages:
/delayed-stream/1.0.0: /delayed-stream/1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'} engines: {node: '>=0.4.0'}
dev: true
/denque/2.1.0: /denque/2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
@ -3347,6 +3356,16 @@ packages:
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
dev: true dev: true
/follow-redirects/1.15.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dev: false
/foreground-child/3.1.1: /foreground-child/3.1.1:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@ -3385,7 +3404,6 @@ packages:
asynckit: 0.4.0 asynckit: 0.4.0
combined-stream: 1.0.8 combined-stream: 1.0.8
mime-types: 2.1.35 mime-types: 2.1.35
dev: true
/formidable/2.1.2: /formidable/2.1.2:
resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==} resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==}
@ -5134,6 +5152,10 @@ packages:
forwarded: 0.2.0 forwarded: 0.2.0
ipaddr.js: 1.9.1 ipaddr.js: 1.9.1
/proxy-from-env/1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
/pump/3.0.0: /pump/3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
dependencies: dependencies:

View File

@ -1,28 +0,0 @@
const nodeResolve = require("@rollup/plugin-node-resolve");
const common = require("@rollup/plugin-commonjs");
const babel = require("@rollup/plugin-babel");
const copy = require("rollup-plugin-copy");
const { join } = require("path");
const esbuild = require('rollup-plugin-esbuild')
module.exports = [
{
input: "hydration/entry.js",
output: [
{
dir: "hydration",
format: "esm"
}
],
preserveEntrySignatures: false,
plugins: [
esbuild.default(),
nodeResolve({ exportConditions: ["solid"] }),
babel({
babelHelpers: "bundled",
presets: [["solid", { generate: "dom", hydratable: true }]]
}),
common(),
]
}
];

File diff suppressed because it is too large Load Diff

10
sql/personaGIE.sql Normal file
View File

@ -0,0 +1,10 @@
use educa7ls_plataforma;
create table personaGIE (
id int auto_increment primary key,
dni varchar(20),
nombres varchar(40),
apellidoPaterno varchar(20),
apellidoMaterno varchar(20)
);

14
sql/registroGIE.sql Normal file
View File

@ -0,0 +1,14 @@
use educa7ls_plataforma;
alter table registroGIE
add personaId int;
alter table registroGIE
add cursoGIEId int;
alter table registroGIE
add constraint registroGIE___fk_personaId
foreign key (personaId) references personaGIE (id);
alter table registroGIE
add constraint registroGIE___fk_cursoGIEId
foreign key (cursoGIEId) references cursosGIE (id);

View File

@ -2,28 +2,30 @@ import { Module } from "@nestjs/common";
import { AppController } from "./app.controller"; import { AppController } from "./app.controller";
import { AppService } from "./app.service"; import { AppService } from "./app.service";
import { TypeOrmModule } from "@nestjs/typeorm"; import { TypeOrmModule } from "@nestjs/typeorm";
import { CertsController } from "./certs/certs.controller"; import { CertificateController } from "./certificate/certificate.controller";
import { ServeStaticModule } from "@nestjs/serve-static"; import { CertificateService } from "./certificate/certificate.service";
import { join } from "path"; import { Persona } from "./model/Persona/persona.entity";
import { CursoGIE } from "./model/CursoGIE/cursoGIE.entity";
import { RegistroGIE } from "./model/RegistroGIE/registroGIE.entity";
@Module({ @Module({
imports: [ imports: [
/*
TypeOrmModule.forRoot({ TypeOrmModule.forRoot({
type: "mysql", type: "mysql",
host: "localhost", host: "localhost",
port: 3306, port: 3306,
username: "root", username: "root",
database: "educa7ls", password: "1234567890",
entities: [], database: "educa7ls_plataforma",
entities: [
Persona,
CursoGIE,
RegistroGIE,
],
synchronize: false, synchronize: false,
}), }),
*/
//ServeStaticModule.forRoot({
// rootPath: join(__dirname, "..", "static"),
//}),
], ],
controllers: [AppController, CertsController], controllers: [AppController, CertificateController],
providers: [AppService], providers: [AppService, CertificateService],
}) })
export class AppModule {} export class AppModule {}

View File

@ -0,0 +1,22 @@
import { Controller, Get, Param } from "@nestjs/common";
import { renderToString } from "solid-js/web";
import { Certs } from "../views/Certs";
import { template } from "./certificate.template";
import { CertificateService } from "./certificate.service";
@Controller("certificate")
export class CertificateController {
constructor(private certificateService: CertificateService) {
}
@Get()
entry(): string {
const html = renderToString(Certs);
return template(html);
}
@Get(":id")
getById(@Param() param: {id: string}) {
return this.certificateService.getByDni(param.id);
}
}

View File

@ -0,0 +1,81 @@
import { Injectable, NotFoundException } from "@nestjs/common";
import { Person } from "../types/Person";
import { DataSource, Repository } from "typeorm";
import { Persona } from "../model/Persona/persona.entity";
import { RegistroGIE } from "../model/RegistroGIE/registroGIE.entity";
import axios from "axios";
interface SunatPerson {
apellidoPaterno: string,
apellidoMaterno: string,
nombres: string,
}
@Injectable()
export class CertificateService {
personaRepository: Repository<Persona>;
registroGIERepository;
constructor(private dataSource: DataSource) {
this.personaRepository = dataSource.getRepository(Persona);
this.registroGIERepository = dataSource.getRepository(RegistroGIE);
}
async getByDni(dni: string): Promise<Person> {
// Search person in new tables
const person = await this.personaRepository.findOneBy({
dni,
});
if (person !== null) {
return {
id: person.id,
apellidoMaterno: person.apellidoMaterno,
apellidoPaterno: person.apellidoPaterno,
nombres: person.nombres,
nombreCompleto: `${person.nombres} ${person.apellidoPaterno} ${person.apellidoMaterno}`,
};
}
// TODO: Move to env variables
const token = "apis-token-1.aTSI1U7KEuT-6bbbCguH-4Y8TI6KS73N";
// Search person in SUNAT API
let personSunat: SunatPerson | null = null;
try {
const personSunatR = await axios.get<SunatPerson>(`https://api.apis.net.pe/v1/dni?numero=${dni}`, {
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json",
"charset": "utf-8",
},
});
personSunat = personSunatR.data;
} catch (e) { /* empty */
}
// If person exists in SUNAT, add to DB and return
if (personSunat !== null) {
const p = new Persona();
p.dni = dni;
p.apellidoPaterno = personSunat.apellidoPaterno;
p.apellidoMaterno = personSunat.apellidoMaterno;
p.nombres = personSunat.nombres;
await this.personaRepository.save(p);
return {
id: p.id,
apellidoPaterno: p.apellidoPaterno,
apellidoMaterno: p.apellidoMaterno,
nombres: p.nombres,
nombreCompleto: `${p.nombres} ${p.apellidoPaterno} ${p.apellidoMaterno}`,
};
}
// 404
throw new NotFoundException("Persona no encontrada", {
description: "La persona no se encontró ni en BD ni en API",
});
}
}

View File

@ -1,13 +0,0 @@
import { Controller, Get } from "@nestjs/common";
import { renderToString } from "solid-js/web";
import { Certs } from "../views/Certs";
import { template } from "./certs.template";
@Controller("certs")
export class CertsController {
@Get()
entry(): string {
const html = renderToString(Certs);
return template(html);
}
}

View File

@ -0,0 +1,16 @@
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
@Entity("cursosGIE")
export class CursoGIE {
@PrimaryGeneratedColumn()
id: number;
@Column("varchar", { length: 50 })
nombre: string;
@Column()
codigo: number;
@Column("varchar", { length: 50 })
otro: string;
}

View File

@ -0,0 +1,26 @@
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from "typeorm";
import { RegistroGIE } from "../RegistroGIE/registroGIE.entity";
@Entity("personaGIE")
export class Persona {
@PrimaryGeneratedColumn()
id: number;
@Column("varchar", { length: 20 })
dni: string;
@Column("varchar", { length: 40 })
nombres: string;
@Column("varchar", { length: 20 })
apellidoPaterno: string;
@Column("varchar", { length: 20 })
apellidoMaterno: string;
//
// Relation to registroGIE
//
@OneToMany(() => RegistroGIE, (register) => register.persona)
registros: Array<RegistroGIE>;
}

View File

@ -0,0 +1,44 @@
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { Persona } from "../Persona/persona.entity";
import { CursoGIE } from "../CursoGIE/cursoGIE.entity";
@Entity("registroGIE")
export class RegistroGIE {
@PrimaryGeneratedColumn()
id: number;
/* Legacy fields */
// Deprecated
@Column("varchar", { length: 20 })
dni: string;
// Deprecated
@Column("varchar", { length: 70 })
nombre: string;
// Deprecated
@Column()
curso: number;
@Column()
codigo: number;
@Column("date")
fecha_actual: Date;
@Column("date")
fecha_inscripcion: Date;
// Deprecated
@Column("varchar", { length: 100 })
curso_nombre: string;
/* New fields */
@ManyToOne(() => Persona, (persona) => persona.registros)
persona: Persona;
@ManyToOne(() => CursoGIE)
cursoGIE: CursoGIE;
}

11
src/types/Person.ts Normal file
View File

@ -0,0 +1,11 @@
export interface Person {
id: number,
/** Full name of the person */
nombreCompleto: string,
/** First last name of the person */
apellidoPaterno: string,
/** Second last name of the person */
apellidoMaterno: string,
/** Names of the person. May be more than 1 word. */
nombres: string,
}

View File

@ -1,17 +1,10 @@
import { createEffect, createSignal } from "solid-js"; import { createEffect, createSignal } from "solid-js";
import { Search } from "./components/Search"; import { Search } from "./components/Search";
import { Person } from "./types/Person"; import { Person } from "../types/Person";
import { Registers } from "./components/Registers"; import { Registers } from "./components/Registers";
export function Certs() { export function Certs() {
const [person, setPerson] = createSignal<Person | null>(null); const [person, setPerson] = createSignal<Person | null>(null);
const [count, setCount] = createSignal(0);
createEffect(() => {
const c = count();
console.log(`IM TRACKING COUNT! ${c}`);
});
return ( return (
<div> <div>
<h1 <h1
@ -19,9 +12,6 @@ export function Certs() {
> >
Registrar certificado Registrar certificado
</h1> </h1>
<p>{count()}</p>
<button onclick={() => setCount((x) => x + 1)}>UP</button>
<hr/>
<Search setPerson={setPerson}/> <Search setPerson={setPerson}/>
<Registers person={person()} /> <Registers person={person()} />
</div> </div>

View File

@ -1,5 +1,5 @@
import { Show, createEffect, createSignal } from "solid-js"; import { Show, createEffect, createSignal } from "solid-js";
import { Person } from "../types/Person"; import { Person } from "../../types/Person";
export function Registers(props: {person: Person | null}) { export function Registers(props: {person: Person | null}) {
const [loading, setLoading] = createSignal(false); const [loading, setLoading] = createSignal(false);
@ -26,7 +26,7 @@ export function Registers(props: {person: Person | null}) {
<input <input
type="text" type="text"
disabled disabled
value={props.person?.nombre} value={props.person?.nombreCompleto}
class="bg-c-background text-c-on-background border-c-outline border rounded px-2 py-1 class="bg-c-background text-c-on-background border-c-outline border rounded px-2 py-1
disabled:cursor-text w-full" disabled:cursor-text w-full"
/> />

View File

@ -1,6 +1,6 @@
import { createSignal } from "solid-js"; import { createSignal } from "solid-js";
import { JSX } from "solid-js/jsx-runtime"; import { JSX } from "solid-js/jsx-runtime";
import { Person } from "../types/Person"; import { Person } from "../../types/Person";
type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & { type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
submitter: HTMLElement; submitter: HTMLElement;
@ -8,40 +8,31 @@ type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
/* /*
Form that retrieves an user from the DB given an ID Form that retrieves a user from the DB given an ID
*/ */
export function Search(props: {setPerson: (p: Person | null) => void}) { export function Search(props: {setPerson: (p: Person | null) => void}) {
const [dni, setDni] = createSignal(""); const [dni, setDni] = createSignal("");
const [loading, setLoading] = createSignal(false); const [loading, setLoading] = createSignal(false);
const [error, setError] = createSignal(""); const [error, setError] = createSignal("");
console.log("This is a log in the function");
/* /*
Sends the DNI to RUST to search in the DB Get the user data from the DB
*/ */
const searchDNI: HTMLEventFn = (ev) => { const searchDNI: HTMLEventFn = (ev) => {
console.log("D:"); console.log("Retrieving from db...");
ev.preventDefault(); ev.preventDefault();
setLoading(true); setLoading(true);
setError(""); setError("");
/* fetch(`/certificate/${dni()}`)
invoke("search_dni", {dni: dni()}) .then((response) => response.json())
.then((p) => { .then((person: Person) => {
const person = p as Person;
props.setPerson(person); props.setPerson(person);
setLoading(false); setLoading(false);
}) setError("");
.catch((error) => {
setError(error);
setLoading(false);
}); });
*/
}; };
console.log(searchDNI);
return ( return (
<div class="p-4"> <div class="p-4">
<h2 class="my-2 font-bold text-xl">1. Buscar persona</h2> <h2 class="my-2 font-bold text-xl">1. Buscar persona</h2>

View File

@ -1,7 +0,0 @@
export type Person = {
/** Full name */
nombre: string
apellidoPaterno: string,
apellidoMaterno: string,
nombres: string,
};

View File

@ -1,22 +1,6 @@
/* /*
Base colors :D Base colors :D
*/ */
@media (prefers-color-scheme: light) {
:root {
--c-primary: #895100;
--c-on-primary: #ffffff;
--c-primary-container: #ffdcbd;
--c-on-primary-container: #2c1600;
--c-background: #fffbff;
--c-on-background: #fffbff;
--c-surface: #fffbff;
--c-on-surface: #201b16;
--c-outline: #837568;
--c-surface-variant: #f2dfd0;
--c-on-surface-variant: #50453a;
}
}
:root { :root {
--c-primary: #ffb86d; --c-primary: #ffb86d;
--c-on-primary: #492900; --c-on-primary: #492900;
@ -35,6 +19,27 @@
--c-on-surface-variant: #d5c3b5; --c-on-surface-variant: #d5c3b5;
} }
@media (prefers-color-scheme: light) {
:root {
--c-primary: #9e4300;
--c-on-primary: #ffffff;
--c-primary-container: #ffdbcb;
--c-on-primary-container: #341100;
--c-error: #ba1a1a;
--c-on-error: #ffffff;
--c-error-container: #ffdad6;
--c-on-error-container: #410002;
--c-background: #fffbff;
--c-on-background: #201a18;
--c-surface: #fffbff;
--c-on-surface: #201a18;
--c-outline: #85736c;
--c-surface-variant: #f4ded4;
--c-on-surface-variant: #52443d;
}
}
body { body {
background-color: var(--c-background); background-color: var(--c-background);
color: var(--c-on-background); color: var(--c-on-background);