Skip to content

Commit 759f2e6

Browse files
authored
feat: get geolocation within item, provide addressLabel (#814)
1 parent 35d77f6 commit 759f2e6

21 files changed

Lines changed: 316 additions & 77 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
"@fastify/view": "7.4.1",
6767
"@fastify/websocket": "7.2.0",
6868
"@graasp/etherpad-api": "2.1.1",
69-
"@graasp/sdk": "3.7.0",
69+
"@graasp/sdk": "3.8.0",
7070
"@graasp/translations": "1.23.0",
7171
"@rapideditor/country-coder": "5.2.2",
7272
"@sentry/node": "7.64.0",
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { MigrationInterface, QueryRunner } from 'typeorm';
2+
3+
export class Migrations1707735413855 implements MigrationInterface {
4+
name = 'geolocation-address-1707735413855';
5+
6+
public async up(queryRunner: QueryRunner): Promise<void> {
7+
await queryRunner.query(
8+
`ALTER TABLE "item_geolocation" ADD "addressLabel" character varying(300)`,
9+
);
10+
}
11+
12+
public async down(queryRunner: QueryRunner): Promise<void> {
13+
await queryRunner.query(`ALTER TABLE "item_geolocation" DROP COLUMN "addressLabel"`);
14+
}
15+
}

src/plugins/mailer/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import pointOfView from '@fastify/view';
66
import { FastifyPluginAsync } from 'fastify';
77
import fp from 'fastify-plugin';
88

9-
import { DEFAULT_LANG } from '@graasp/sdk';
9+
import { DEFAULT_LANG } from '@graasp/translations';
1010

1111
import i18next from './i18n';
1212
import { applyLayout } from './layout';

src/services/auth/plugins/magicLink/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { StatusCodes } from 'http-status-codes';
22

33
import { FastifyPluginAsync } from 'fastify';
44

5-
import { DEFAULT_LANG, RecaptchaAction } from '@graasp/sdk';
5+
import { RecaptchaAction } from '@graasp/sdk';
6+
import { DEFAULT_LANG } from '@graasp/translations';
67

78
import { AUTH_CLIENT_HOST } from '../../../../utils/config';
89
import { MemberAlreadySignedUp } from '../../../../utils/errors';

src/services/auth/plugins/magicLink/service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { promisify } from 'util';
44

55
import { FastifyBaseLogger, FastifyInstance } from 'fastify';
66

7-
import { DEFAULT_LANG } from '@graasp/sdk';
7+
import { DEFAULT_LANG } from '@graasp/translations';
88

99
import { JWT_SECRET } from '../../../../utils/config';
1010
import {

src/services/auth/plugins/mobile/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { StatusCodes } from 'http-status-codes';
22

33
import { FastifyPluginAsync } from 'fastify';
44

5-
import { DEFAULT_LANG, RecaptchaAction } from '@graasp/sdk';
5+
import { RecaptchaAction } from '@graasp/sdk';
6+
import { DEFAULT_LANG } from '@graasp/translations';
67

78
import { MOBILE_DEEP_LINK_PROTOCOL } from '../../../../utils/config';
89
import { buildRepositories } from '../../../../utils/repositories';

src/services/auth/plugins/mobile/service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { promisify } from 'util';
55

66
import { FastifyBaseLogger, FastifyInstance } from 'fastify';
77

8-
import { DEFAULT_LANG } from '@graasp/sdk';
8+
import { DEFAULT_LANG } from '@graasp/translations';
99

1010
import { JWT_SECRET } from '../../../../utils/config';
1111
import {

src/services/item/plugins/geolocation/ItemGeolocation.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ export class ItemGeolocation extends BaseEntity {
3333
@Column({ type: 'float' })
3434
lng: number;
3535

36+
@Column({ length: 300, nullable: true })
37+
addressLabel: string;
38+
3639
@Column({ type: 'character varying', nullable: true, length: 4 })
3740
country: string | null;
3841

src/services/item/plugins/geolocation/errors.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,16 @@ export class PartialItemGeolocation extends GraaspItemGeolocationError {
3535
);
3636
}
3737
}
38+
39+
export class MissingGeolocationSearchParams extends GraaspItemGeolocationError {
40+
constructor(data?) {
41+
super(
42+
{
43+
code: 'GIGEOERR003',
44+
statusCode: StatusCodes.BAD_REQUEST,
45+
message: 'Geolocation Search should include parent item, or all lat1, lat2, lng1 and lng2',
46+
},
47+
data,
48+
);
49+
}
50+
}

src/services/item/plugins/geolocation/index.test.ts

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { saveItemAndMembership } from '../../../itemMembership/test/fixtures/mem
1111
import { saveMember } from '../../../member/test/fixtures/members';
1212
import { savePublicItem } from '../../test/fixtures/items';
1313
import { ItemGeolocation } from './ItemGeolocation';
14-
import { ItemGeolocationNotFound } from './errors';
1514

1615
// mock datasource
1716
jest.mock('../../../../plugins/datasource');
@@ -114,13 +113,13 @@ describe('Item Geolocation', () => {
114113
});
115114
});
116115

117-
it('Throws if no geolocation', async () => {
116+
it('Return null if no geolocation', async () => {
118117
const res = await app.inject({
119118
method: HttpMethod.GET,
120119
url: `${ITEMS_ROUTE_PREFIX}/${item.id}/geolocation`,
121120
});
122-
expect(res.statusCode).toBe(StatusCodes.NOT_FOUND);
123-
expect(res.json()).toMatchObject(new ItemGeolocationNotFound(expect.anything()));
121+
expect(res.statusCode).toBe(StatusCodes.OK);
122+
expect(res.json()).toBeNull();
124123
});
125124

126125
it('Throws if id is not a uuid', async () => {
@@ -228,6 +227,51 @@ describe('Item Geolocation', () => {
228227
expect(res.json()).toHaveLength(3);
229228
expectItemGeolocations(res.json(), [geoloc1, geoloc2, geoloc3]);
230229
});
230+
231+
it('Get item geolocations within parent item', async () => {
232+
const { item: parentItem } = await saveItemAndMembership({ member: actor });
233+
const { item: item1 } = await saveItemAndMembership({
234+
item: { name: 'hello bye' },
235+
member: actor,
236+
parentItem,
237+
});
238+
const geoloc1 = await repository.save({ item: item1, lat: 1, lng: 2, country: 'de' });
239+
const { item: item2 } = await saveItemAndMembership({
240+
item: { description: 'hello bye' },
241+
member: actor,
242+
parentItem,
243+
});
244+
const geoloc2 = await repository.save({ item: item2, lat: 1, lng: 2, country: 'de' });
245+
const { item: item3 } = await saveItemAndMembership({
246+
item: { name: 'bye hello' },
247+
member: actor,
248+
});
249+
await repository.save({ item: item3, lat: 1, lng: 2, country: 'de' });
250+
251+
const res = await app.inject({
252+
method: HttpMethod.GET,
253+
url: `${ITEMS_ROUTE_PREFIX}/geolocation?parentItemId=${parentItem.id}`,
254+
});
255+
expect(res.statusCode).toBe(StatusCodes.OK);
256+
expect(res.json()).toHaveLength(2);
257+
expectItemGeolocations(res.json(), [geoloc1, geoloc2]);
258+
});
259+
260+
it('Throw for incorrect parent item id', async () => {
261+
const res = await app.inject({
262+
method: HttpMethod.GET,
263+
url: `${ITEMS_ROUTE_PREFIX}/geolocation?lat1=1&lat2=1&lng1=1&lng2=2&parentItemId=incorrect-id`,
264+
});
265+
expect(res.statusCode).toBe(StatusCodes.BAD_REQUEST);
266+
});
267+
268+
it('Throw if no parent item id and no lat lng', async () => {
269+
const res = await app.inject({
270+
method: HttpMethod.GET,
271+
url: `${ITEMS_ROUTE_PREFIX}/geolocation`,
272+
});
273+
expect(res.statusCode).toBe(StatusCodes.BAD_REQUEST);
274+
});
231275
});
232276
});
233277

0 commit comments

Comments
 (0)