From e461932aa97a719b157b2aecbf4cfd95524f7802 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A2u=20Cao?= Date: Mon, 29 Jun 2026 19:24:25 +0200 Subject: [PATCH] Get all places from a list --- README.md | 4 ++++ src/places.ts | 28 ++++++++++++++++++++++ test/places.test.ts | 58 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) diff --git a/README.md b/README.md index eaac4f1..1358873 100644 --- a/README.md +++ b/README.md @@ -101,6 +101,10 @@ await places.lists.getAll(); // Get specific list await places.lists.get('to-do'); + +// Get all places from a list +await places.lists.getPlaces('to-do'); +``` ``` ## API Reference diff --git a/src/places.ts b/src/places.ts index 7e6ccee..1743776 100644 --- a/src/places.ts +++ b/src/places.ts @@ -154,6 +154,13 @@ export interface PlacesClient { */ get(id: string): Promise; + /** + * Get all places from a list. + * @param listId - The slug ID of the list. + * @returns Array of Place objects. + */ + getPlaces(listId: string): Promise; + /** * Create or update a list. * @param id - The slug ID (e.g., "to-go"). @@ -249,6 +256,27 @@ const Places = function ( return privateClient.getObject(path) as Promise; }, + async getPlaces(listId: string): Promise { + const list = await this.get(listId); + if (!list) { + throw new Error(`List not found: ${listId}`); + } + + if (!list.placeRefs || !Array.isArray(list.placeRefs)) { + return []; + } + + const promises = list.placeRefs.map(async (ref: any) => { + if (!ref.id || !ref.geohash) return null; + const path = getPath(ref.geohash, ref.id); + const place = await privateClient.getObject(path); + return place as Place | null; + }); + + const results = await Promise.all(promises); + return results.filter((p): p is Place => !!p); + }, + async create(id: string, title: string, color?: string): Promise { const path = `_lists/${id}`; let list = (await privateClient.getObject(path)) as List; diff --git a/test/places.test.ts b/test/places.test.ts index 7acaee0..01974fa 100644 --- a/test/places.test.ts +++ b/test/places.test.ts @@ -203,6 +203,64 @@ describe('Places Module', () => { }); }); + describe('getPlaces', () => { + it('returns all places from a list', async () => { + const mockList = { + id: 'hiking', + title: 'Hiking', + placeRefs: [ + { id: 'place-1', geohash: 'u33dc0' }, + { id: 'place-2', geohash: 'w1q789' }, + ], + }; + + const mockPlace1 = { id: 'place-1', geohash: 'u33dc0', title: 'Hiking Trail' }; + const mockPlace2 = { id: 'place-2', geohash: 'w1q789', title: 'Mountain Peak' }; + + mockClient.getObject.mockImplementation(async (path: string) => { + if (path === '_lists/hiking') return mockList; + if (path === 'u3/3d/place-1') return mockPlace1; + if (path === 'w1/q7/place-2') return mockPlace2; + return null; + }); + + const result = await lists.getPlaces('hiking'); + expect(mockClient.getObject).toHaveBeenCalledWith('_lists/hiking'); + expect(mockClient.getObject).toHaveBeenCalledWith('u3/3d/place-1'); + expect(mockClient.getObject).toHaveBeenCalledWith('w1/q7/place-2'); + expect(result).toEqual([mockPlace1, mockPlace2]); + }); + + it('throws an error if the list does not exist', async () => { + mockClient.getObject.mockResolvedValue(null); + + await expect(lists.getPlaces('non-existent')).rejects.toThrow('List not found: non-existent'); + expect(mockClient.getObject).toHaveBeenCalledWith('_lists/non-existent'); + }); + + it('filters out any places that failed to load or are missing', async () => { + const mockList = { + id: 'hiking', + title: 'Hiking', + placeRefs: [ + { id: 'place-1', geohash: 'u33dc0' }, + { id: 'place-2', geohash: 'w1q789' }, + ], + }; + + const mockPlace1 = { id: 'place-1', geohash: 'u33dc0', title: 'Place 1' }; + + mockClient.getObject.mockImplementation(async (path: string) => { + if (path === '_lists/hiking') return mockList; + if (path === 'u3/3d/place-1') return mockPlace1; + return null; // place-2 is missing + }); + + const result = await lists.getPlaces('hiking'); + expect(result).toEqual([mockPlace1]); + }); + }); + describe('create', () => { it('stores a new list when none exists', async () => { const now = '2023-01-01T00:00:00.000Z';