From d0cbd6ab0a5df5dc2c0303307e2587702d35af81 Mon Sep 17 00:00:00 2001 From: dskvr Date: Thu, 14 Dec 2023 17:34:59 +0100 Subject: [PATCH] add more results to each ISO standard, options object and more tests --- src/index.test.ts | 91 ++++++++++++++++++++++++++++++++---- src/index.ts | 116 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 181 insertions(+), 26 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 5f088e7..45dc622 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,6 +1,9 @@ import { describe, it, expect } from 'vitest'; import ngeotags from './index'; // Adjust the import path as needed +import { iso31661, iso31662, iso31663, ISO31661AssignedEntry, ISO31661Entry } from 'iso-3166'; + + describe('ngeotags', () => { it('should throw an error if input is null', () => { expect(() => ngeotags(null)).toThrow('Input is required'); @@ -13,6 +16,7 @@ describe('ngeotags', () => { it('should correctly transform gps coordinates', () => { const result = ngeotags({ lat: 47.5636, lon: 19.0947 }); expect(result).toContainEqual(['g', '47.5636,19.0947', 'gps']); + console.log('result:', result) }); it('should correctly transform geohash', () => { @@ -22,15 +26,45 @@ describe('ngeotags', () => { }); it('should correctly transform ISO-3166-1 country name', () => { - const result = ngeotags({ countryCode: 'HU' }); - const countryName = result.find(tag => tag[2] === 'ISO-3166-1'); - expect(countryName).toBeDefined(); + const result = ngeotags({ countryCode: 'HU' }, { iso31661: true }); + const countryNames = result.filter(tag => tag?.[3] && tag[3].includes('ISO-3166-1')); + // console.log('result:', result) + // console.log('filtered:', countryNames) + expect(countryNames.length).toBe(4); }); it('should correctly transform ISO-3166-2 region code', () => { - const result = ngeotags({ country: 'HU', regionName: 'Budapest' }); - const regionCode = result.find(tag => tag[2] === 'ISO-3166-2'); - expect(regionCode).toBeDefined(); + const result = ngeotags({ country: 'HU', regionName: 'Budapest' }, {iso31662: true}); + const regionCodes = result.filter(tag => tag?.[3] && tag[3]?.includes('ISO-3166-2')); + expect(regionCodes.length).toBe(3) + }); + + it('should correctly transform ISO-3166-1 data', () => { + const result = ngeotags({ countryCode: 'HU' }, { iso31661: true }); + // console.log('result', result) + expect(result).toContainEqual(['g', 'HU', 'countryCode', 'ISO-3166-1:alpha2']); + expect(result).toContainEqual(['g', 'HUN', 'countryCode', 'ISO-3166-1:alpha3']); + expect(result).toContainEqual(['g', 'HUN', 'countryCode', 'ISO-3166-1:alpha3']); + expect(result).toContainEqual(['g', 'HUN', 'countryCode', 'ISO-3166-1:alpha3']); + // Add checks for numeric and name as well + }); + + it('should correctly transform ISO-3166-3 changes', () => { + // const countryData = iso31661.find((c: ISO31661Entry) => c.alpha2 === "AI"); + // console.log(countryData) + const result = ngeotags({ countryCode: 'AI' }, { iso31663: true }); // Assuming 'AN' has an ISO 3166-3 change + expect(result).toContainEqual([ 'g', 'DJ', 'countryCode', 'ISO-3166-3:alpha2' ]); + expect(result).toContainEqual( [ 'g', 'AIA', 'countryCode', 'ISO-3166-3:alpha3' ]); + expect(result).toContainEqual([ 'g', '660', 'countryCode', 'ISO-3166-3:numeric' ]); + expect(result).toContainEqual([ 'g', 'Anguilla', 'countryCode', 'ISO-3166-3:name' ]); + // Add checks for other fields as needed + }); + + it('should correctly transform ISO-3166-2 data', () => { + const result = ngeotags({ country: 'HU', regionName: 'Budapest' }, { iso31662: true }); + expect(result).toContainEqual(['g', 'HU-BU', 'regionCode', 'ISO-3166-2:code']); + expect(result).toContainEqual(['g', 'Budapest', 'regionCode', 'ISO-3166-2:name']); + expect(result).toContainEqual(['g', 'HU', 'regionCode', 'ISO-3166-2:parent']); }); it('should correctly transform city name', () => { @@ -48,8 +82,49 @@ describe('ngeotags', () => { expect(result).toContainEqual(['g', 'EU', 'continentCode']); }); - it('should include Earth as planet by default', () => { - const result = ngeotags({}); + it('should include not include Earth as planet by default', () => { + const result = ngeotags({}, {}).find(tag => tag[2] === 'planet') || []; + expect(result.length).toBe(0); + }); + + it('should include should Earth when enabled', () => { + const result = ngeotags({}, {planet: true}); expect(result).toContainEqual(['g', 'Earth', 'planet']); }); + + it('should correctly transform geohashes of diminishing resolution', () => { + const result = ngeotags({ lat: 47.5636, lon: 19.0947 }); + const geohashTags = result.filter(tag => tag[2] === 'geohash'); + expect(geohashTags.length).toBeGreaterThan(1); // Expect multiple geohash resolutions + }); + + it('should return all possible geotags', () => { + const input = { + "status": "success", + "continent": "Europe", + "continentCode": "EU", + "country": "Hungary", + "countryCode": "HU", + "region": "BU", + "regionName": "Budapest", + "city": "Budapest", + "district": "", + "zip": "1124", + "lat": 47.5636, + "lon": 19.0947, + "timezone": "Europe/Budapest", + "offset": 3600, + "currency": "HUF", + "isp": "Magyar Telekom", + "org": "", + "as": "AS5483 Magyar Telekom plc.", + "asname": "MAGYAR-TELEKOM-MAIN-AS", + "mobile": false, + "proxy": false, + "hosting": false + } + const result = ngeotags(input, { iso31661: true, iso31662: true, iso31663: true, planet: true }); + console.log('FULL result:', result) + expect(result.length).toBeGreaterThan(10); + }) }); diff --git a/src/index.ts b/src/index.ts index 791e761..98210ad 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,5 @@ import ngeohash from 'ngeohash'; -import { iso31661, iso31662, ISO31661Entry } from 'iso-3166'; +import { iso31661, iso31662, iso31663, ISO31661AssignedEntry, ISO31661Entry } from 'iso-3166'; interface InputData { lat?: number; @@ -13,13 +13,60 @@ interface InputData { [key: string]: any; } -const generateTags = (input: InputData): Array<[string, string, string]> => { - const tags: Array<[string, string, string]> = []; +interface Options { + iso31661?: boolean, + iso31662?: boolean, + iso31663?: boolean, + planet?: boolean, + geohash?: boolean, + gps?: boolean, + city?: boolean, + country?: boolean, + region?: boolean, + continent?: boolean, + continentCode?: boolean, +} + +// interface ISO31661Entry { +// alpha2: string; +// alpha3: string; +// numeric: string; +// name: string; +// } + - tags.push([`g`, `${input.lat},${input.lon}`, 'gps']); +type ISO31663FieldType = 'alpha2' | 'alpha3' | 'numeric' | 'name'; + +const getUpdatedIso31663Value = (type: ISO31663FieldType, code: string): string => { + const matchedEntry = iso31663.find(entry => + entry.from[type] === code || + entry.to.some(change => change[type] === code) + ); + if (matchedEntry) { + if (matchedEntry.from[type] === code) { + // If the code matches the 'from' part, return the new value ('to' part) + return matchedEntry.to[0][type]; // Assuming there's always at least one 'to' entry + } else { + // If the code matches the 'to' part, return the same code as it's already the new value + return code; + } + } + + // If there's no match, return the original code + return code; +}; + + +const generateTags = (input: InputData, opts: Options): Array<[string, string, string] | [string, string, string, string]> => { + const tags: Array<[string, string, string] | [string, string, string, string]> = []; + + // GPS + if (opts.gps && input.lat && input.lon) { + tags.push([`g`, `${input.lat},${input.lon}`, 'gps']); + } // Geohash - if (input.lat && input.lon) { + if (opts.geohash && input.lat && input.lon) { const fullGeohash = ngeohash.encode(input.lat, input.lon); // Generate geohashes of diminishing resolution @@ -28,49 +75,82 @@ const generateTags = (input: InputData): Array<[string, string, string]> => { tags.push(['g', partialGeohash, 'geohash']); } } - + // ISO-3166-1 Alpha-2 (country code) - if (input.countryCode) { - const countryData = iso31661.find((c: ISO31661Entry) => c.alpha2 === input.countryCode); + if (opts.iso31661 && input.countryCode) { + const countryData = iso31661.find((c: ISO31661AssignedEntry) => c.alpha2 === input.countryCode); if (countryData) { - tags.push(['g', countryData.name, 'ISO-3166-1']); + (['alpha2', 'alpha3', 'numeric', 'name'] as const).forEach((type) => { + // Make sure type is a valid key of ISO31661AssignedEntry + tags.push(['g', countryData[type as keyof ISO31661AssignedEntry], 'countryCode', `ISO-3166-1:${type}`]); + }); } } // ISO-3166-2 (region code) - if (input.country && input.regionName) { + if (opts.iso31662 && input.country && input.regionName) { const regionData = iso31662.find(r => r.parent === input.country && r.name === input.regionName); if (regionData) { - tags.push(['g', regionData.code, 'ISO-3166-2']); + ['code', 'name', 'parent'].forEach((type) => { + tags.push(['g', regionData[type as keyof typeof regionData], 'regionCode', `ISO-3166-2:${type}`]); + }); + } + } + + // ISO-3166-3 (changes) + if (opts.iso31663 && input.countryCode) { + const countryData = iso31661.find((c: ISO31661Entry) => c.alpha2 === input.countryCode); + if (countryData) { + (['alpha2', 'alpha3', 'numeric', 'name'] as const).forEach((type) => { + // Here we assert that type is a key of ISO31661Entry + const value = countryData[type as keyof ISO31661Entry]; + tags.push(['g', getUpdatedIso31663Value(type, value), 'countryCode', `ISO-3166-3:${type}`]); + }); } } // City - if (input.city) { + if (opts.city && input.city) { tags.push(['g', input.city, 'city']); } // Continent - if (input.continent) { + if (opts.continent && input.continent) { tags.push(['g', input.continent, 'continent']); } // Continent Code - if (input.continentCode) { + if (opts.continentCode && input.continentCode) { tags.push(['g', input.continentCode, 'continentCode']); } // Planet - Assuming Earth as there's no specific data for planet - tags.push(['g', 'Earth', 'planet']); + if(opts.planet) { + tags.push(['g', 'Earth', 'planet']); + } return tags; }; -export default (input: InputData | null): Array<[string, string, string]> => { +export default (input: InputData | null, opts?: Options): Array<[string, string, string] | [string, string, string, string]> => { if (!input) throw new Error('Input is required'); if (typeof input !== 'object') throw new Error('Input must be an object'); + opts = { + iso31661: false, + iso31662: true, + iso31663: false, + planet: false, + geohash: true, + gps: true, + city: true, + country: true, + region: true, + continent: true, + continentCode: true, + ...opts + }; - return generateTags(input); -}; + return generateTags(input, opts); +}; \ No newline at end of file