Skip to content

Commit 9fdeb2d

Browse files
authored
MIM-537: Get residence data by rental id (#311)
* add residence by rental id route * type updates * add tests for new route
1 parent d60395a commit 9fdeb2d

File tree

8 files changed

+421
-2
lines changed

8 files changed

+421
-2
lines changed

src/adapters/property-base-adapter/generated/api-types.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,62 @@ export interface paths {
210210
patch?: never;
211211
trace?: never;
212212
};
213+
"/residences/rental-id/{rentalId}": {
214+
parameters: {
215+
query?: never;
216+
header?: never;
217+
path?: never;
218+
cookie?: never;
219+
};
220+
/**
221+
* Get a residence by rental ID
222+
* @description Returns a residence with the specified rental ID
223+
*/
224+
get: {
225+
parameters: {
226+
query?: never;
227+
header?: never;
228+
path: {
229+
/** @description The rental ID of the residence */
230+
rentalId: string;
231+
};
232+
cookie?: never;
233+
};
234+
requestBody?: never;
235+
responses: {
236+
/** @description Successfully retrieved the residence */
237+
200: {
238+
headers: {
239+
[name: string]: unknown;
240+
};
241+
content: {
242+
"application/json": components["schemas"]["GetResidenceByRentalIdResponse"];
243+
};
244+
};
245+
/** @description Residence not found */
246+
404: {
247+
headers: {
248+
[name: string]: unknown;
249+
};
250+
content?: never;
251+
};
252+
/** @description Internal server error */
253+
500: {
254+
headers: {
255+
[name: string]: unknown;
256+
};
257+
content?: never;
258+
};
259+
};
260+
};
261+
put?: never;
262+
post?: never;
263+
delete?: never;
264+
options?: never;
265+
head?: never;
266+
patch?: never;
267+
trace?: never;
268+
};
213269
"/residences/{id}": {
214270
parameters: {
215271
query?: never;
@@ -1342,6 +1398,95 @@ export interface components {
13421398
disableQuantitiesBelowCompany: number;
13431399
timestamp: string;
13441400
};
1401+
ResidenceByRentalId: {
1402+
id: string;
1403+
code: string;
1404+
name: string | null;
1405+
accessibility: {
1406+
wheelchairAccessible: boolean;
1407+
elevator: boolean;
1408+
};
1409+
features: {
1410+
hygieneFacility: string | null;
1411+
};
1412+
entrance: string | null;
1413+
deleted: boolean;
1414+
type: {
1415+
code: string;
1416+
name: string | null;
1417+
roomCount: number | null;
1418+
kitchen: number;
1419+
};
1420+
rentalInformation: {
1421+
apartmentNumber: string | null;
1422+
rentalId: string | null;
1423+
type: {
1424+
code: string;
1425+
name: string | null;
1426+
};
1427+
} | null;
1428+
property: {
1429+
id: string | null;
1430+
name: string | null;
1431+
code: string | null;
1432+
};
1433+
building: {
1434+
id: string | null;
1435+
name: string | null;
1436+
code: string | null;
1437+
};
1438+
areaSize: number | null;
1439+
};
1440+
GetResidenceByRentalIdResponse: {
1441+
content: {
1442+
id: string;
1443+
code: string;
1444+
name: string | null;
1445+
accessibility: {
1446+
wheelchairAccessible: boolean;
1447+
elevator: boolean;
1448+
};
1449+
features: {
1450+
hygieneFacility: string | null;
1451+
};
1452+
entrance: string | null;
1453+
deleted: boolean;
1454+
type: {
1455+
code: string;
1456+
name: string | null;
1457+
roomCount: number | null;
1458+
kitchen: number;
1459+
};
1460+
rentalInformation: {
1461+
apartmentNumber: string | null;
1462+
rentalId: string | null;
1463+
type: {
1464+
code: string;
1465+
name: string | null;
1466+
};
1467+
} | null;
1468+
property: {
1469+
id: string | null;
1470+
name: string | null;
1471+
code: string | null;
1472+
};
1473+
building: {
1474+
id: string | null;
1475+
name: string | null;
1476+
code: string | null;
1477+
};
1478+
areaSize: number | null;
1479+
};
1480+
_links: {
1481+
self: {
1482+
href: string;
1483+
};
1484+
link: {
1485+
href: string;
1486+
templated: boolean;
1487+
};
1488+
};
1489+
};
13451490
};
13461491
responses: never;
13471492
parameters: never;

src/adapters/property-base-adapter/index.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,39 @@ export async function getResidenceDetails(
199199
}
200200
}
201201

202+
type GetResidenceByRentalIdResponse =
203+
components['schemas']['GetResidenceByRentalIdResponse']['content']
204+
205+
export async function getResidenceByRentalId(
206+
rentalId: string
207+
): Promise<
208+
AdapterResult<GetResidenceByRentalIdResponse, 'not-found' | 'unknown'>
209+
> {
210+
try {
211+
const fetchResponse = await client().GET(
212+
'/residences/rental-id/{rentalId}',
213+
{
214+
params: { path: { rentalId: rentalId } },
215+
}
216+
)
217+
218+
if (fetchResponse.data?.content) {
219+
return { ok: true, data: fetchResponse.data.content }
220+
}
221+
222+
if (fetchResponse.response.status === 404) {
223+
return { ok: false, err: 'not-found' }
224+
}
225+
226+
throw new Error(
227+
`Unexpected response status: ${fetchResponse.response.status}`
228+
)
229+
} catch (err) {
230+
logger.error({ err }, 'property-base-adapter.getResidenceByRentalId')
231+
return { ok: false, err: 'unknown' }
232+
}
233+
}
234+
202235
type GetStaircasesResponse = components['schemas']['Staircase'][]
203236

204237
export async function getStaircases(

src/adapters/tests/property-base-adapter.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,60 @@ describe('property-base-adapter', () => {
189189
})
190190
})
191191

192+
describe('getResidenceByRentalId', () => {
193+
it('returns err if request fails', async () => {
194+
const residenceDetailsMock = factory.residenceByRentalIdDetails.build()
195+
196+
mockServer.use(
197+
http.get(
198+
`${config.propertyBaseService.url}/residences/rental-id/1234`,
199+
() => new HttpResponse(null, { status: 500 })
200+
)
201+
)
202+
203+
const result = await propertyBaseAdapter.getResidenceByRentalId('1234')
204+
205+
expect(result.ok).toBe(false)
206+
if (!result.ok) expect(result.err).toBe('unknown')
207+
})
208+
209+
it('returns not-found if residence is not found', async () => {
210+
mockServer.use(
211+
http.get(
212+
`${config.propertyBaseService.url}/residences/rental-id/1234`,
213+
() => new HttpResponse(null, { status: 404 })
214+
)
215+
)
216+
217+
const result = await propertyBaseAdapter.getResidenceByRentalId('1234')
218+
219+
expect(result.ok).toBe(false)
220+
if (!result.ok) expect(result.err).toBe('not-found')
221+
})
222+
223+
it('returns residence', async () => {
224+
const residenceDetailsMock = factory.residenceByRentalIdDetails.build()
225+
mockServer.use(
226+
http.get(
227+
`${config.propertyBaseService.url}/residences/rental-id/1234`,
228+
() =>
229+
HttpResponse.json(
230+
{
231+
content: residenceDetailsMock,
232+
},
233+
{ status: 200 }
234+
)
235+
)
236+
)
237+
238+
const result = await propertyBaseAdapter.getResidenceByRentalId('1234')
239+
240+
expect(result).toMatchObject({
241+
ok: true,
242+
data: residenceDetailsMock,
243+
})
244+
})
245+
})
192246
describe('getResidenceDetails', () => {
193247
it('returns err if request fails', async () => {
194248
const residenceDetailsMock = factory.residenceDetails.build()

src/services/property-base-service/index.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { logger, generateRouteMetadata } from 'onecore-utilities'
77
import { registerSchema } from '../../utils/openapi'
88
import * as schemas from './schemas'
99
import { calculateResidenceStatus } from './calculate-residence-status'
10+
import { z } from 'zod'
1011

1112
/**
1213
* @swagger
@@ -30,6 +31,10 @@ export const routes = (router: KoaRouter) => {
3031
registerSchema('ResidenceDetails', schemas.ResidenceDetailsSchema)
3132
registerSchema('Staircase', schemas.StaircaseSchema)
3233
registerSchema('Room', schemas.RoomSchema)
34+
registerSchema(
35+
'ResidenceByRentalIdDetails',
36+
schemas.ResidenceByRentalIdSchema
37+
)
3338

3439
/**
3540
* @swagger
@@ -341,6 +346,81 @@ export const routes = (router: KoaRouter) => {
341346
}
342347
})
343348

349+
/**
350+
* @swagger
351+
* /propertyBase/residence/rental-id/{rentalId}:
352+
* get:
353+
* summary: Get residence data by residence rental id
354+
* tags:
355+
* - Property base Service
356+
* description: Retrieves residence data by residence rental id
357+
* parameters:
358+
* - in: path
359+
* name: rentalId
360+
* required: true
361+
* schema:
362+
* type: string
363+
* description: Rental id for the residence to fetch
364+
* responses:
365+
* '200':
366+
* description: Successfully retrieved residence.
367+
* content:
368+
* application/json:
369+
* schema:
370+
* type: object
371+
* properties:
372+
* content:
373+
* $ref: '#/components/schemas/ResidenceByRentalIdDetails'
374+
* '404':
375+
* description: Residence not found
376+
* content:
377+
* application/json:
378+
* schema:
379+
* type: object
380+
* properties:
381+
* error:
382+
* type: string
383+
* example: Residence not found
384+
* '500':
385+
* description: Internal server error. Failed to retrieve residence data.
386+
* content:
387+
* application/json:
388+
* schema:
389+
* type: object
390+
* properties:
391+
* error:
392+
* type: string
393+
* example: Internal server error
394+
* security:
395+
* - bearerAuth: []
396+
*/
397+
router.get('(.*)/propertyBase/residence/rental-id/:rentalId', async (ctx) => {
398+
const metadata = generateRouteMetadata(ctx)
399+
const { rentalId } = ctx.params
400+
401+
const getResidence =
402+
await propertyBaseAdapter.getResidenceByRentalId(rentalId)
403+
404+
if (!getResidence.ok) {
405+
if (getResidence.err === 'not-found') {
406+
ctx.status = 404
407+
ctx.body = { error: 'Residence not found', ...metadata }
408+
return
409+
}
410+
411+
logger.error(getResidence.err, 'Internal server error', metadata)
412+
ctx.status = 500
413+
ctx.body = { error: 'Internal server error', ...metadata }
414+
return
415+
}
416+
417+
ctx.status = 200
418+
ctx.body = {
419+
content: getResidence.data satisfies schemas.ResidenceByRentalIdDetails,
420+
...metadata,
421+
}
422+
})
423+
344424
/**
345425
* @swagger
346426
* /propertyBase/residence/{residenceId}:

0 commit comments

Comments
 (0)