import { Injectable, NotFoundException } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { ILike, Repository } from 'typeorm'; import { Country } from './countries.entity'; import { intOrNull } from 'src/utils/int-or-null'; const COUNTRIES_URL = 'https://download.geonames.org/export/dump/countryInfo.txt'; const ACCEPT_FIELDS = [ 'iso', 'iso3', 'isoNumeric', 'fips', 'country', 'capital', 'area', 'population', 'continent', 'tld', 'currencyCode', 'currencyName', 'phone', 'postalCodeFormat', 'postalCodeRegex', 'languages', 'geonameid', 'neighbours', 'equivalentFipsCode', ]; @Injectable() export class CountriesService { constructor( @InjectRepository(Country, 'geobase') private countryRepository: Repository, ) {} private mapAllowedQuery(select: string[]) { return (Array.isArray(select) ? select : [select]).filter((field) => ACCEPT_FIELDS.includes(field), ) as unknown as (keyof Country)[]; } async pullCountries() { const countryList = await fetch(COUNTRIES_URL).then((x) => x.text()); const entries = countryList .split('\n') .filter((line) => line && !line.startsWith('#')) .map((line) => line.replace('\r', '').split('\t')) .map((entry) => { const country = new Country(); Object.assign(country, { iso: entry[0], iso3: entry[1], isoNumeric: entry[2], fips: entry[3], country: entry[4], capital: entry[5], area: parseFloat(entry[6] || '0'), population: intOrNull(entry[7]), continent: entry[8], tld: entry[9], currencyCode: entry[10], currencyName: entry[11], phone: entry[12], postalCodeFormat: entry[13], postalCodeRegex: entry[14], languages: entry[15], geonameid: intOrNull(entry[16]), neighbours: entry[17], equivalentFipsCode: entry[18], }); return country; }); await this.countryRepository.save(entries); } async getAll(fields = ACCEPT_FIELDS) { const select = this.mapAllowedQuery(fields); return this.countryRepository.find({ select, }); } async search(query?: string, fields = ACCEPT_FIELDS) { const select = this.mapAllowedQuery(fields); const filter = {}; if (query) { filter['country'] = ILike(`%${query}%`); } return this.countryRepository.find({ where: filter, select, }); } async getByISO(iso: string, fields = ACCEPT_FIELDS) { const select = this.mapAllowedQuery(fields); const find = await this.countryRepository.findOne({ where: { [iso.length === 2 ? 'iso' : 'iso3']: iso, }, select, }); if (!find) { throw new NotFoundException(); } return find; } }