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

@ -18,7 +18,7 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
"ssr:watch": "node esbuild.js",
"ssr:hydration": "node esbuild-client.js",
"ssr:hydration": "node esbuild-client.js",
"tailwind": "tailwindcss -i static/tailwind.css -o ./static/styles.css --watch"
},
"dependencies": {
@ -27,6 +27,7 @@
"@nestjs/platform-express": "^9.0.0",
"@nestjs/serve-static": "^3.0.1",
"@nestjs/typeorm": "^9.0.1",
"axios": "^1.4.0",
"mysql2": "^3.2.4",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",

View File

@ -17,6 +17,7 @@ specifiers:
'@typescript-eslint/eslint-plugin': ^5.0.0
'@typescript-eslint/parser': ^5.0.0
autoprefixer: ^10.4.14
axios: ^1.4.0
esbuild: ^0.17.18
esbuild-plugin-solid: ^0.5.0
eslint: ^8.0.1
@ -48,6 +49,7 @@ dependencies:
'@nestjs/platform-express': 9.4.0_gqkob5jndcfeb3wjw72wbdwyne
'@nestjs/serve-static': 3.0.1_gqkob5jndcfeb3wjw72wbdwyne
'@nestjs/typeorm': 9.0.1_5jgqalkub5zb475ngukfuhbndu
axios: 1.4.0
mysql2: 3.3.0
reflect-metadata: 0.1.13
rxjs: 7.8.1
@ -2183,7 +2185,6 @@ packages:
/asynckit/0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: true
/atomic-sleep/1.0.0:
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
@ -2216,6 +2217,16 @@ packages:
- supports-color
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:
resolution: {integrity: sha512-mA4eCDh5mSo2EcA9xQjVTpmbbNk32Zb3Q3QFQsNhaK56Q+yoXowzFodLux30HRgyOho5rsQ6B0P9QpMkvvnJ0Q==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@ -2617,7 +2628,6 @@ packages:
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: true
/commander/2.20.3:
resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
@ -2762,7 +2772,6 @@ packages:
/delayed-stream/1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: true
/denque/2.1.0:
resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==}
@ -3347,6 +3356,16 @@ packages:
resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
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:
resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
engines: {node: '>=14'}
@ -3385,7 +3404,6 @@ packages:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: true
/formidable/2.1.2:
resolution: {integrity: sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==}
@ -5134,6 +5152,10 @@ packages:
forwarded: 0.2.0
ipaddr.js: 1.9.1
/proxy-from-env/1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
/pump/3.0.0:
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
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 { AppService } from "./app.service";
import { TypeOrmModule } from "@nestjs/typeorm";
import { CertsController } from "./certs/certs.controller";
import { ServeStaticModule } from "@nestjs/serve-static";
import { join } from "path";
import { CertificateController } from "./certificate/certificate.controller";
import { CertificateService } from "./certificate/certificate.service";
import { Persona } from "./model/Persona/persona.entity";
import { CursoGIE } from "./model/CursoGIE/cursoGIE.entity";
import { RegistroGIE } from "./model/RegistroGIE/registroGIE.entity";
@Module({
imports: [
/*
TypeOrmModule.forRoot({
type: "mysql",
host: "localhost",
port: 3306,
username: "root",
database: "educa7ls",
entities: [],
password: "1234567890",
database: "educa7ls_plataforma",
entities: [
Persona,
CursoGIE,
RegistroGIE,
],
synchronize: false,
}),
*/
//ServeStaticModule.forRoot({
// rootPath: join(__dirname, "..", "static"),
//}),
],
controllers: [AppController, CertsController],
providers: [AppService],
controllers: [AppController, CertificateController],
providers: [AppService, CertificateService],
})
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 { Search } from "./components/Search";
import { Person } from "./types/Person";
import { Person } from "../types/Person";
import { Registers } from "./components/Registers";
export function Certs() {
const [person, setPerson] = createSignal<Person | null>(null);
const [count, setCount] = createSignal(0);
createEffect(() => {
const c = count();
console.log(`IM TRACKING COUNT! ${c}`);
});
return (
<div>
<h1
@ -19,9 +12,6 @@ export function Certs() {
>
Registrar certificado
</h1>
<p>{count()}</p>
<button onclick={() => setCount((x) => x + 1)}>UP</button>
<hr/>
<Search setPerson={setPerson}/>
<Registers person={person()} />
</div>

View File

@ -1,5 +1,5 @@
import { Show, createEffect, createSignal } from "solid-js";
import { Person } from "../types/Person";
import { Person } from "../../types/Person";
export function Registers(props: {person: Person | null}) {
const [loading, setLoading] = createSignal(false);
@ -26,7 +26,7 @@ export function Registers(props: {person: Person | null}) {
<input
type="text"
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
disabled:cursor-text w-full"
/>

View File

@ -1,6 +1,6 @@
import { createSignal } from "solid-js";
import { JSX } from "solid-js/jsx-runtime";
import { Person } from "../types/Person";
import { Person } from "../../types/Person";
type HTMLEventFn = JSX.EventHandlerUnion<HTMLFormElement, Event & {
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}) {
const [dni, setDni] = createSignal("");
const [loading, setLoading] = createSignal(false);
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) => {
console.log("D:");
console.log("Retrieving from db...");
ev.preventDefault();
setLoading(true);
setError("");
/*
invoke("search_dni", {dni: dni()})
.then((p) => {
const person = p as Person;
fetch(`/certificate/${dni()}`)
.then((response) => response.json())
.then((person: Person) => {
props.setPerson(person);
setLoading(false);
})
.catch((error) => {
setError(error);
setLoading(false);
setError("");
});
*/
};
console.log(searchDNI);
return (
<div class="p-4">
<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
*/
@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 {
--c-primary: #ffb86d;
--c-on-primary: #492900;
@ -35,6 +19,27 @@
--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 {
background-color: var(--c-background);
color: var(--c-on-background);