Skip to content

Commit 98d1f5e

Browse files
author
Divyansh Singh
authored
feat(geo): add getStatesByZipCode export to geo module [ROW-574] (#232)
* feat(geo): add getStatesByZipCode export to geo module * fix(docs): add getStatesByZipCode API documentation * Create quick-mayflies-poke.md * docs: add code comments for better readability
1 parent d8de170 commit 98d1f5e

File tree

5 files changed

+508
-0
lines changed

5 files changed

+508
-0
lines changed

.changeset/quick-mayflies-poke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@razorpay/i18nify-js": patch
3+
---
4+
5+
feat(geo): add getStatesByZipCode export to geo module [ROW-574]

packages/i18nify-js/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -919,6 +919,52 @@ getStates('XYZ').catch((err) => {
919919
}); // Outputs Invalid country code: XYZ
920920
```
921921

922+
#### getStatesByZipCode(zipcode, countryCode?)
923+
924+
🏘️ Want to find out which state a zipcode belongs to? The getStatesByZipCode API is your trusty guide! Just provide a zipcode, and optionally a country code, and it'll fetch the state information for you. If you don't specify a country, it'll search across all supported countries to find your zipcode's home state. Perfect for address validation, location-based services, or just satisfying your geographical curiosity! 🌍🔍
925+
926+
##### Examples
927+
928+
```javascript
929+
// Finding state for a specific zipcode in a country
930+
const stateInfo = await getStatesByZipCode('110001', 'IN');
931+
console.log(stateInfo);
932+
/*
933+
{
934+
code: 'DL',
935+
name: 'Delhi',
936+
country: 'IN'
937+
}
938+
*/
939+
940+
// Searching across all supported countries
941+
const stateInfo = await getStatesByZipCode('110001');
942+
console.log(stateInfo);
943+
/*
944+
{
945+
code: 'DL',
946+
name: 'Delhi',
947+
country: 'IN'
948+
}
949+
*/
950+
951+
// Handling invalid zipcodes
952+
try {
953+
await getStatesByZipCode('999999', 'IN');
954+
} catch (error) {
955+
console.error(error.message);
956+
// Outputs: Zipcode "999999" not found in any supported country. Please ensure you provide a valid zipcode that exists within the specified countries.
957+
}
958+
959+
// Handling invalid country codes
960+
try {
961+
await getStatesByZipCode('110001', 'XX');
962+
} catch (error) {
963+
console.error(error.message);
964+
// Outputs: Invalid country code: XX. Please ensure you provide a valid country code.
965+
}
966+
```
967+
922968
#### getCities(country_code, states_code)
923969

924970
Uncover the charm of cities worldwide with the getCities API! This dynamic tool fetches an array of cities complete with their names, time zones, and region names, providing a detailed glimpse into urban life across the globe.
Lines changed: 336 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,336 @@
1+
import getStatesByZipCode from '../getStatesByZipCode';
2+
import { CountryCodeType } from '../../types/geo';
3+
import { INDIA_DATA } from '../mocks/country';
4+
import * as shared from '../../shared';
5+
6+
describe('getStatesByZipCode', () => {
7+
beforeEach(() => {
8+
// Reset all mocks before each test
9+
jest.resetAllMocks();
10+
// Reset the supported countries mock
11+
jest.restoreAllMocks();
12+
13+
global.fetch = jest.fn(() =>
14+
Promise.resolve({
15+
ok: true,
16+
status: 200,
17+
json: () => Promise.resolve(INDIA_DATA),
18+
} as Response),
19+
);
20+
});
21+
22+
it('fetches state info for a valid zipcode', async () => {
23+
const mockResponse = {
24+
states: {
25+
DL: {
26+
name: 'Delhi',
27+
cities: {
28+
'New Delhi': {
29+
name: 'New Delhi',
30+
timezone: 'Asia/Kolkata',
31+
zipcodes: ['110001', '110002', '110003'],
32+
'region_name/district_name': 'Central Delhi',
33+
},
34+
},
35+
},
36+
},
37+
};
38+
39+
global.fetch = jest.fn().mockImplementation(() =>
40+
Promise.resolve({
41+
ok: true,
42+
json: () => Promise.resolve(mockResponse),
43+
}),
44+
);
45+
46+
const stateInfo = await getStatesByZipCode(
47+
'110001',
48+
'IN' as CountryCodeType,
49+
);
50+
51+
expect(stateInfo).toEqual({
52+
code: 'DL',
53+
name: 'Delhi',
54+
country: 'IN',
55+
});
56+
expect(fetch).toHaveBeenCalledTimes(1);
57+
expect(fetch).toHaveBeenCalledWith(
58+
expect.stringContaining('/country/subdivisions/IN.json'),
59+
);
60+
});
61+
62+
it('handles empty zipcode', async () => {
63+
await expect(
64+
getStatesByZipCode('', 'IN' as CountryCodeType),
65+
).rejects.toThrow('Zipcode is required. Please provide a valid zipcode.');
66+
});
67+
68+
it('handles invalid country code', async () => {
69+
// Mock the supported countries list to only include IN
70+
jest
71+
.spyOn(shared.I18NIFY_DATA_SUPPORTED_COUNTRIES, 'includes')
72+
.mockImplementation((code) => code === 'IN');
73+
74+
await expect(
75+
getStatesByZipCode('110001', 'XX' as CountryCodeType),
76+
).rejects.toThrow(
77+
'Invalid country code: XX. Please ensure you provide a valid country code.',
78+
);
79+
});
80+
81+
it('handles zipcode not found', async () => {
82+
const mockResponse = {
83+
states: {
84+
DL: {
85+
name: 'Delhi',
86+
cities: {
87+
'New Delhi': {
88+
name: 'New Delhi',
89+
timezone: 'Asia/Kolkata',
90+
zipcodes: ['110001', '110002'],
91+
'region_name/district_name': 'Central Delhi',
92+
},
93+
},
94+
},
95+
},
96+
};
97+
98+
global.fetch = jest.fn().mockImplementation(() =>
99+
Promise.resolve({
100+
ok: true,
101+
json: () => Promise.resolve(mockResponse),
102+
}),
103+
);
104+
105+
await expect(
106+
getStatesByZipCode('999999', 'IN' as CountryCodeType),
107+
).rejects.toThrow(
108+
'Zipcode "999999" not found in any supported country. Please ensure you provide a valid zipcode that exists within the specified countries.',
109+
);
110+
});
111+
112+
it('handles API errors', async () => {
113+
global.fetch = jest.fn().mockImplementation(() =>
114+
Promise.resolve({
115+
ok: false,
116+
status: 404,
117+
json: () => {
118+
throw new Error('res.json is not a function');
119+
},
120+
}),
121+
);
122+
123+
await expect(
124+
getStatesByZipCode('110001', 'IN' as CountryCodeType),
125+
).rejects.toThrow(
126+
'An error occurred while fetching state data. The error details are: res.json is not a function',
127+
);
128+
});
129+
130+
it('handles network errors', async () => {
131+
global.fetch = jest
132+
.fn()
133+
.mockImplementation(() => Promise.reject(new Error('Network error')));
134+
135+
await expect(
136+
getStatesByZipCode('110001', 'IN' as CountryCodeType),
137+
).rejects.toThrow(
138+
'An error occurred while fetching state data. The error details are: Network error',
139+
);
140+
});
141+
142+
it('searches across all supported countries when country code is not provided', async () => {
143+
const mockResponses = {
144+
IN: {
145+
states: {
146+
DL: {
147+
name: 'Delhi',
148+
cities: {
149+
'New Delhi': {
150+
name: 'New Delhi',
151+
timezone: 'Asia/Kolkata',
152+
zipcodes: ['110001', '110002'],
153+
'region_name/district_name': 'Central Delhi',
154+
},
155+
},
156+
},
157+
},
158+
},
159+
US: {
160+
states: {
161+
CA: {
162+
name: 'California',
163+
cities: {
164+
'Los Angeles': {
165+
name: 'Los Angeles',
166+
timezone: 'America/Los_Angeles',
167+
zipcodes: ['90001', '90002'],
168+
'region_name/district_name': 'Los Angeles County',
169+
},
170+
},
171+
},
172+
},
173+
},
174+
};
175+
176+
// Mock the supported countries array to only include IN and US
177+
const mockSupportedCountries = ['IN', 'US'];
178+
Object.defineProperty(shared, 'I18NIFY_DATA_SUPPORTED_COUNTRIES', {
179+
value: mockSupportedCountries,
180+
writable: true,
181+
});
182+
183+
global.fetch = jest.fn().mockImplementation((url) => {
184+
const countryCode = url.split('/').pop()?.split('.')[0];
185+
const response = mockResponses[countryCode as keyof typeof mockResponses];
186+
if (!response) {
187+
return Promise.resolve({
188+
ok: true,
189+
json: () => Promise.resolve({ states: {} }),
190+
});
191+
}
192+
return Promise.resolve({
193+
ok: true,
194+
json: () => Promise.resolve(response),
195+
});
196+
});
197+
198+
const stateInfo = await getStatesByZipCode('110001');
199+
200+
expect(stateInfo).toEqual({
201+
code: 'DL',
202+
name: 'Delhi',
203+
country: 'IN',
204+
});
205+
expect(fetch).toHaveBeenCalledTimes(2); // Called for both IN and US
206+
});
207+
208+
it('handles zipcode with leading/trailing spaces', async () => {
209+
const mockResponse = {
210+
states: {
211+
DL: {
212+
name: 'Delhi',
213+
cities: {
214+
'New Delhi': {
215+
name: 'New Delhi',
216+
timezone: 'Asia/Kolkata',
217+
zipcodes: ['110001', '110002'],
218+
'region_name/district_name': 'Central Delhi',
219+
},
220+
},
221+
},
222+
},
223+
};
224+
225+
global.fetch = jest.fn().mockImplementation(() =>
226+
Promise.resolve({
227+
ok: true,
228+
json: () => Promise.resolve(mockResponse),
229+
}),
230+
);
231+
232+
const stateInfo = await getStatesByZipCode(
233+
' 110001 ',
234+
'IN' as CountryCodeType,
235+
);
236+
237+
expect(stateInfo).toEqual({
238+
code: 'DL',
239+
name: 'Delhi',
240+
country: 'IN',
241+
});
242+
});
243+
244+
it('handles empty states in country data', async () => {
245+
const mockResponse = {
246+
states: {},
247+
};
248+
249+
global.fetch = jest.fn().mockImplementation(() =>
250+
Promise.resolve({
251+
ok: true,
252+
json: () => Promise.resolve(mockResponse),
253+
}),
254+
);
255+
256+
await expect(
257+
getStatesByZipCode('110001', 'IN' as CountryCodeType),
258+
).rejects.toThrow(
259+
'Zipcode "110001" not found in any supported country. Please ensure you provide a valid zipcode that exists within the specified countries.',
260+
);
261+
});
262+
263+
it('handles empty cities in state data', async () => {
264+
const mockResponse = {
265+
states: {
266+
DL: {
267+
name: 'Delhi',
268+
cities: {},
269+
},
270+
},
271+
};
272+
273+
global.fetch = jest.fn().mockImplementation(() =>
274+
Promise.resolve({
275+
ok: true,
276+
json: () => Promise.resolve(mockResponse),
277+
}),
278+
);
279+
280+
await expect(
281+
getStatesByZipCode('110001', 'IN' as CountryCodeType),
282+
).rejects.toThrow(
283+
'Zipcode "110001" not found in any supported country. Please ensure you provide a valid zipcode that exists within the specified countries.',
284+
);
285+
});
286+
287+
it('handles empty zipcodes array in city data', async () => {
288+
const mockResponse = {
289+
states: {
290+
DL: {
291+
name: 'Delhi',
292+
cities: {
293+
'New Delhi': {
294+
name: 'New Delhi',
295+
timezone: 'Asia/Kolkata',
296+
zipcodes: [],
297+
'region_name/district_name': 'Central Delhi',
298+
},
299+
},
300+
},
301+
},
302+
};
303+
304+
global.fetch = jest.fn().mockImplementation(() =>
305+
Promise.resolve({
306+
ok: true,
307+
json: () => Promise.resolve(mockResponse),
308+
}),
309+
);
310+
311+
await expect(
312+
getStatesByZipCode('110001', 'IN' as CountryCodeType),
313+
).rejects.toThrow(
314+
'Zipcode "110001" not found in any supported country. Please ensure you provide a valid zipcode that exists within the specified countries.',
315+
);
316+
});
317+
318+
it('handles malformed country data', async () => {
319+
const mockResponse = {
320+
// Missing states property
321+
};
322+
323+
global.fetch = jest.fn().mockImplementation(() =>
324+
Promise.resolve({
325+
ok: true,
326+
json: () => Promise.resolve(mockResponse),
327+
}),
328+
);
329+
330+
await expect(
331+
getStatesByZipCode('110001', 'IN' as CountryCodeType),
332+
).rejects.toThrow(
333+
"An error occurred while fetching state data. The error details are: Cannot read properties of undefined (reading 'states')",
334+
);
335+
});
336+
});

0 commit comments

Comments
 (0)