diff --git a/src/controller/person/SunatQueue.ts b/src/controller/person/SunatQueue.ts new file mode 100644 index 0000000..31cc7a5 --- /dev/null +++ b/src/controller/person/SunatQueue.ts @@ -0,0 +1,56 @@ + +type ResolveFn = (v: null) => void; +const waitTime = 500; + +const queue: Array> = []; + +function schedule(resolve: ResolveFn) { + if (queue.length === 0) { + // Call resolve after 500ms + const newPromise = new Promise((newResolve) => { + setTimeout(() => { + // Remove this promise from the queue + queue.shift(); + + // Resolve the original promise + resolve(null); + + // Resolve this promise, + // to allow chaining + newResolve(null); + }, waitTime); + }); + + queue.push(newPromise); + } else { + // Create a new promise, and make it wait + // for the last promise in the queue + const lastPromise = queue[queue.length - 1]; + + const newPromise = new Promise((newResolve) => { + // Chain to last promise + lastPromise.then(() => { + setTimeout(() => { + // Remove this promise from the queue + queue.shift(); + + // Resolve the original promise + resolve(null); + + // Resolve this promise, + // to allow chaining + newResolve(null); + }, waitTime); + }); + }); + + queue.push(newPromise); + } +} + +export function waitForSunatApi(): Promise { + return new Promise((resolve) => { + schedule(resolve); + }); +} + diff --git a/src/controller/person/person.service.ts b/src/controller/person/person.service.ts index 4046ceb..f06f65e 100644 --- a/src/controller/person/person.service.ts +++ b/src/controller/person/person.service.ts @@ -4,6 +4,7 @@ import { DataSource, Repository } from "typeorm"; import { Persona } from "../../model/Persona/persona.entity"; import { Person } from "../../types/Person"; import axios from "axios"; +import { waitForSunatApi } from "./SunatQueue"; interface SunatPerson { apellidoPaterno: string, @@ -34,6 +35,15 @@ export class PersonService { } } + /** + * ALL CALLS TO THIS METHOD ARE INSERTED TO A QUEUE, + * EACH REQUEST TO SUNAT WAITS AT LEAST 500ms, + * to avoid hitting rate limits. + * + * @param dni Dni of the person + * @returns A person, if found, or throws if not found + * @throws A 404 exception if not found + */ async getByDni(dni: string): Promise { // Search person in new tables const person = await this.personaRepository.findOneBy({ @@ -57,6 +67,10 @@ export class PersonService { // Search person in SUNAT API let personSunat: SunatPerson | null = null; try { + // Enter Sunat API queue + await waitForSunatApi(); + + // Then perform the actual request const personSunatR = await axios.get(`https://api.apis.net.pe/v1/dni?numero=${dni}`, { headers: { "Authorization": `Bearer ${token}`,