Skip to content

Commit 241140d

Browse files
authored
Merge pull request #76 from Bostads-AB-Mimer/feature/mim-546-skapa-endpoint-for-att-hamta-bilplats
MIM-546: Create endpoint to fetch parking space on rental id
2 parents 5218504 + b247038 commit 241140d

File tree

10 files changed

+951
-676
lines changed

10 files changed

+951
-676
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
model Address {
2+
addressId String @id @map("keycmadr") @db.Char(15) // Adress-ID
3+
addressType String @map("keycmtyp") @db.Char(15) // Adresstyp
4+
tableName String @map("keydbtbl") @db.Char(15) // Tabell
5+
code String @map("keycode") @db.Char(15)
6+
region String @db.Char(2)
7+
streetAddress String? @map("adress1") @db.VarChar(60) // Postadress
8+
streetAddress2 String? @map("adress2") @db.VarChar(60) // Postadress 2
9+
postalCode String? @map("adress3") @db.VarChar(60) // Postnr
10+
city String? @map("adress4") @db.VarChar(60) // Ort
11+
country String? @map("adress5") @db.VarChar(60) // Land
12+
streetName String? @map("adress6") @db.VarChar(60) // Gata
13+
streetNumber String? @map("adress7") @db.VarChar(60) // Gatunummer
14+
suffix String? @map("adress8") @db.VarChar(60) // Suffix
15+
addressExtra1 String? @map("adress9") @db.VarChar(60)
16+
addressExtra2 String? @map("adress10") @db.VarChar(60)
17+
validFrom DateTime? @map("fdate") // Fr.o.m. datum
18+
validTo DateTime? @map("tdate") // T.o.m. datum
19+
recordStamp String @map("timestamp") @db.Char(10)
20+
21+
@@index([addressType], map: "fkcmadrcmtyp")
22+
@@index([code], map: "fkcmadrcode")
23+
@@index([tableName], map: "fkcmadrdbtbl")
24+
@@index([addressType, tableName, code, validFrom, validTo], name: "akcmadr1")
25+
@@index([addressType, tableName, validFrom, validTo, code, addressId, streetAddress], name: "incmadr_2PU0NKCW5")
26+
27+
@@map("cmadr")
28+
}

packages/property-base/prisma/schema/ParkingSpace_babps.prisma

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ model ParkingSpace {
2020
2121
// Relations
2222
parkingSpaceType ParkingSpaceType @relation(fields: [parkingSpaceTypeId], references: [id], onUpdate: NoAction, map: "fkbabpskeybabpt ")
23+
24+
// This is the relation from property structure to parking space relation
25+
PropertyStructure PropertyStructure[]
26+
2327
// TODO: Commenting this relation until the model 'cmobj' is defined somewhere
2428
// cmobj cmobj @relation(fields: [objectId], references: [keycmobj], onDelete: Cascade, onUpdate: NoAction, map: "fkbabpskeycmobj ")
2529
@@ -30,4 +34,5 @@ model ParkingSpace {
3034
@@index([code], map: "inbabps_2BR0UA5RD")
3135
@@index([name], map: "inbabps_2BR0UA5T7")
3236
@@map("babps")
37+
3338
}

packages/property-base/prisma/schema/PropertyStructure_babuf.prisma

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ model PropertyStructure {
6464
toDate DateTime @map("tdate") @db.DateTime
6565
timestamp String @map("timestamp") @db.Char(10)
6666
67+
parkingSpace ParkingSpace? @relation(fields: [parkingSpaceId], references: [id], onDelete: NoAction, onUpdate: NoAction)
6768
propertyObject PropertyObject @relation(fields: [propertyObjectId], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "fkbabufkeycmobj")
6869
maintenanceUnit MaintenanceUnit? @relation(fields: [propertyObjectId], references: [propertyObjectId], onDelete: NoAction, onUpdate: NoAction)
6970
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { prisma } from './db'
2+
3+
export async function getParkingSpaceByRentalPropertyId(
4+
rentalPropertyId: string
5+
) {
6+
/**
7+
* Example SQL query to fetch parking space information (based on query found in property-management-adapter.ts):
8+
*
9+
* SELECT
10+
* babps.keycmobj,
11+
* babuf.hyresid AS rentalObjectCode,
12+
* babps.code AS vehiclespacecode,
13+
* babps.caption AS vehiclespacecaption,
14+
* babuf.cmpcode AS companycode,
15+
* babuf.cmpcaption AS companycaption,
16+
* babuf.fencode AS scegcode,
17+
* babuf.fencaption AS scegcaption,
18+
* babuf.fstcode AS estatecode,
19+
* babuf.fstcaption AS estatecaption,
20+
* babuf.bygcode AS blockcode,
21+
* babuf.bygcaption AS blockcaption,
22+
* babpt.code AS vehiclespacetypecode,
23+
* babpt.caption AS vehiclespacetypecaption,
24+
* babps.platsnr AS vehiclespacenumber,
25+
* cmadr.adress1 AS postaladdress,
26+
* cmadr.adress2 AS street,
27+
* cmadr.adress3 AS zipcode,
28+
* cmadr.adress4 AS city
29+
* FROM babps
30+
* INNER JOIN babuf ON babuf.keycmobj = babps.keycmobj
31+
* INNER JOIN babpt ON babpt.keybabpt = babps.keybabpt
32+
* LEFT JOIN cmadr ON cmadr.keycode = babps.keycmobj
33+
* AND cmadr.keydbtbl = '_RQA11RNMA'
34+
* AND cmadr.keycmtyp = 'adrpost'
35+
* WHERE babuf.cmpcode = '001' AND babuf.hyresid = '216-704-00-0034';
36+
*/
37+
38+
const propertyStructureResponse = await prisma.propertyStructure.findFirst({
39+
select: {
40+
rentalId: true,
41+
companyCode: true,
42+
companyName: true,
43+
managementUnitCode: true,
44+
managementUnitName: true,
45+
propertyCode: true,
46+
propertyName: true,
47+
buildingCode: true,
48+
buildingName: true,
49+
parkingSpace: {
50+
select: {
51+
propertyObjectId: true,
52+
code: true,
53+
name: true,
54+
parkingNumber: true,
55+
parkingSpaceType: {
56+
select: {
57+
code: true,
58+
name: true,
59+
},
60+
},
61+
},
62+
},
63+
},
64+
where: {
65+
rentalId: rentalPropertyId,
66+
companyCode: '001', // Including this value from the original SQL query as it seems hardcoded
67+
},
68+
})
69+
70+
// Don't return anything if no parking space is found in the data..
71+
if (!propertyStructureResponse?.parkingSpace) return null
72+
73+
// Fetch the address associated with the parking space
74+
const addressResponse = await prisma.address.findFirst({
75+
select: {
76+
streetAddress: true,
77+
streetAddress2: true,
78+
postalCode: true,
79+
city: true,
80+
},
81+
where: {
82+
code: propertyStructureResponse?.parkingSpace?.propertyObjectId,
83+
tableName: '_RQA11RNMA',
84+
addressType: 'adrpost',
85+
},
86+
})
87+
88+
return {
89+
...propertyStructureResponse,
90+
address: {
91+
...addressResponse,
92+
},
93+
}
94+
}

packages/property-base/src/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { routes as componentsRoutes } from './routes/components-route'
33
import { routes as residencesRoutes } from './routes/residences-route'
44
import { routes as buildingsRoutes } from './routes/buildings-route'
55
import { routes as propertiesRoutes } from './routes/properties-route'
6+
import { routes as parkingSpacesRoutes } from './routes/parking-spaces-route'
67
import { routes as staircasesRoutes } from './routes/staircases-route'
78
import { routes as roomsRoutes } from './routes/rooms-route'
89
import { routes as companiesRoutes } from './routes/companies-route'
@@ -15,6 +16,7 @@ componentsRoutes(router)
1516
residencesRoutes(router)
1617
buildingsRoutes(router)
1718
propertiesRoutes(router)
19+
parkingSpacesRoutes(router)
1820
staircasesRoutes(router)
1921
roomsRoutes(router)
2022
companiesRoutes(router)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import KoaRouter from '@koa/router'
2+
import { logger, generateRouteMetadata } from 'onecore-utilities'
3+
import * as parkingSpacesAdapter from '../adapters/parking-spaces-adapter'
4+
import { ParkingSpaceSchema } from '@src/types/parking-space'
5+
import { trimStrings } from '@src/utils/data-conversion'
6+
7+
/**
8+
* @swagger
9+
* openapi: 3.0.0
10+
* tags:
11+
* - name: Parking Spaces
12+
* description: Operations related to parking spaces
13+
*/
14+
export const routes = (router: KoaRouter) => {
15+
/**
16+
* @swagger
17+
* /parking-spaces/by-rental-id/{id}:
18+
* get:
19+
* summary: Gets a list of parking space by rental id
20+
* description: |
21+
* Retrieves parking space from rental id.
22+
* tags:
23+
* - Parking Spaces
24+
* parameters:
25+
* - in: path
26+
* name: id
27+
* required: true
28+
* schema:
29+
* type: string
30+
* description: The rental id.
31+
* responses:
32+
* 200:
33+
* description: |
34+
* Successfully retrieved the parking space. Returns parking space object.
35+
* content:
36+
* application/json:
37+
* schema:
38+
* type: object
39+
* properties:
40+
* content:
41+
* $ref: '#/components/schemas/ParkingSpace'
42+
* 400:
43+
* description: Invalid id provided
44+
* 404:
45+
* description: No parking spaces found for the specified id
46+
* 500:
47+
* description: Internal server error
48+
*/
49+
router.get('(.*)/parking-spaces/by-rental-id/:id', async (ctx) => {
50+
const id = ctx.params.id
51+
const metadata = generateRouteMetadata(ctx)
52+
logger.info(`GET /parking-spaces/by-rental-id/${id}`, metadata)
53+
54+
try {
55+
// Fetch the parking space associated with the rental property id
56+
const parkingSpace = await parkingSpacesAdapter
57+
.getParkingSpaceByRentalPropertyId(id)
58+
.then(trimStrings)
59+
60+
if (!parkingSpace) {
61+
ctx.status = 404
62+
ctx.body = {
63+
reason: 'No parking space found for the specified rental id',
64+
...metadata,
65+
}
66+
return
67+
}
68+
69+
ctx.status = 200
70+
ctx.body = {
71+
content: ParkingSpaceSchema.parse(parkingSpace),
72+
...metadata,
73+
}
74+
} catch (err) {
75+
logger.error({ err }, 'Error fetching parking space by rental id')
76+
ctx.status = 500
77+
const errorMessage = err instanceof Error ? err.message : 'unknown error'
78+
ctx.body = { reason: errorMessage, ...metadata }
79+
}
80+
})
81+
}

packages/property-base/src/routes/swagger-route.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { PropertySchema, PropertyDetailsSchema } from '../types/property'
1414
import { StaircaseSchema } from '../types/staircase'
1515
import { RoomSchema } from '../types/room'
1616
import { CompanySchema, CompanyDetailsSchema } from '../types/company'
17+
import { ParkingSpaceSchema } from '../types/parking-space'
1718
import zodToJsonSchema from 'zod-to-json-schema'
1819
import { MaintenanceUnitSchema } from '../types/maintenance-unit'
1920

@@ -73,6 +74,10 @@ const schemas = {
7374
name: 'GetResidenceByRentalIdResponse',
7475
target: 'openApi3',
7576
}).definitions,
77+
...zodToJsonSchema(ParkingSpaceSchema, {
78+
name: 'ParkingSpace',
79+
target: 'openApi3',
80+
}).definitions,
7681
}
7782

7883
swaggerSpec.definition.components = {

packages/property-base/src/swagger.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const swaggerSpec = {
1414
`${basePath}/routes/residences-route.{ts,js}`,
1515
`${basePath}/routes/buildings-route.{ts,js}`,
1616
`${basePath}/routes/properties-route.{ts,js}`,
17+
`${basePath}/routes/parking-spaces-route.{ts,js}`,
1718
`${basePath}/routes/staircases-route.{ts,js}`,
1819
`${basePath}/routes/rooms-route.{ts,js}`,
1920
`${basePath}/routes/companies-route.{ts,js}`,
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { z } from 'zod'
2+
3+
export const ParkingSpaceSchema = z.object({
4+
rentalId: z.string(),
5+
companyCode: z.string(),
6+
companyName: z.string(),
7+
managementUnitCode: z.string(),
8+
managementUnitName: z.string(),
9+
propertyCode: z.string(),
10+
propertyName: z.string(),
11+
buildingCode: z.string().nullable(),
12+
buildingName: z.string().nullable(),
13+
parkingSpace: z.object({
14+
propertyObjectId: z.string(),
15+
code: z.string(),
16+
name: z.string(),
17+
parkingNumber: z.string(),
18+
parkingSpaceType: z.object({
19+
code: z.string(),
20+
name: z.string(),
21+
}),
22+
}),
23+
address: z
24+
.object({
25+
streetAddress: z.string().nullable(),
26+
streetAddress2: z.string().nullable(),
27+
postalCode: z.string().nullable(),
28+
city: z.string().nullable(),
29+
})
30+
.nullable(),
31+
})
32+
33+
export type ParkingSpace = z.infer<typeof ParkingSpaceSchema>

0 commit comments

Comments
 (0)