Skip to content

Commit d0b674a

Browse files
mittistormenmomentiris0x4d
authored
Develop uthyrning bostader (#285)
* UTH-164: review status is now an enum (#236) * review status is now an enum * adapt to type changes * send all relevant payload for both medportalen and mimer.nu until further notice * bump onecore-types * fix: supposed to be request params not response params (#248) * supposed to be request params not response params * bump onecore types * fix: use v1 schemas for application profile (#249) * fix: use v1 schemas for application profile * bump type * assert with correct schema * UTH-209: Revise application profile schemas and endpoints (#256) * adapt to application profile schema changes * admin and client update * put route helpers in file * update swagger * different return schemas for /admin and /client endpoints, add tests to verify correct field updates * remove duplicate property * Removed comment as update request parameter for application-profile --------- Co-authored-by: Johanna Grahn <johanna.grahn@iteam.se> * chore: bump onecore-types * fix: update .env.template to remove half open string * UTH-198 - unbreak application profile (#259) * add back old endpoint for posting application oprofile * alter schemas for backwards compatibility * move route handler to separate 'old' file * remove redundant property logic --------- Co-authored-by: Andreas Lundqvist <31646645+momentiris@users.noreply.github.com> Co-authored-by: Andreas Lundqvist <momentiris@gmail.com> Co-authored-by: 0x4d <0x4d@undefined>
1 parent 69cc1c1 commit d0b674a

File tree

14 files changed

+760
-127
lines changed

14 files changed

+760
-127
lines changed

.env.template

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ EMAIL_ADDRESSES__TENANT_DEFAULT=
66
EMAIL_ADDRESSES__DEV=
77
ELASTICSEARCH_LOGGING_HOST=
88
APPLICATION_NAME=core
9-
MINA_SIDOR__URL='
9+
MINA_SIDOR__URL=

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/adapters/leasing-adapter/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -530,8 +530,8 @@ const validatePropertyRentalRules = async (
530530
}
531531
}
532532

533-
type GetApplicationProfileResponseData = z.infer<
534-
typeof leasing.GetApplicationProfileResponseDataSchema
533+
export type GetApplicationProfileResponseData = z.infer<
534+
typeof leasing.v1.GetApplicationProfileResponseDataSchema
535535
>
536536

537537
async function getApplicationProfileByContactCode(
@@ -560,11 +560,11 @@ async function getApplicationProfileByContactCode(
560560
}
561561

562562
type CreateOrUpdateApplicationProfileResponseData = z.infer<
563-
typeof leasing.CreateOrUpdateApplicationProfileResponseDataSchema
563+
typeof leasing.v1.CreateOrUpdateApplicationProfileResponseDataSchema
564564
>
565565

566566
export type CreateOrUpdateApplicationProfileRequestParams = z.infer<
567-
typeof leasing.CreateOrUpdateApplicationProfileRequestParamsSchema
567+
typeof leasing.v1.CreateOrUpdateApplicationProfileRequestParamsSchema
568568
>
569569

570570
async function createOrUpdateApplicationProfileByContactCode(

src/adapters/tests/leasing-adapter/index.test.ts

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { HttpStatusCode } from 'axios'
22
import assert from 'node:assert'
33
import nock from 'nock'
4+
import { leasing, WaitingListType } from 'onecore-types'
45

56
import config from '../../../common/config'
67
import * as leasingAdapter from '../../leasing-adapter'
@@ -10,7 +11,6 @@ import {
1011
mockedProblematicInvoices,
1112
} from './mocks'
1213
import * as factory from '../../../../test/factories'
13-
import { leasing, WaitingListType } from 'onecore-types'
1414

1515
describe('leasing-adapter', () => {
1616
describe(leasingAdapter.getInternalCreditInformation, () => {
@@ -227,7 +227,17 @@ describe('leasing-adapter', () => {
227227
const result =
228228
await leasingAdapter.createOrUpdateApplicationProfileByContactCode(
229229
'123',
230-
{ expiresAt: new Date(), numAdults: 0, numChildren: 0 }
230+
{
231+
expiresAt: new Date(),
232+
numAdults: 0,
233+
numChildren: 0,
234+
housingType: 'RENTAL',
235+
housingTypeDescription: null,
236+
landlord: null,
237+
lastUpdatedAt: new Date(),
238+
housingReference:
239+
factory.applicationProfileHousingReference.build(),
240+
}
231241
)
232242

233243
expect(result).toEqual({ ok: false, err: 'bad-params' })
@@ -241,7 +251,17 @@ describe('leasing-adapter', () => {
241251
const result =
242252
await leasingAdapter.createOrUpdateApplicationProfileByContactCode(
243253
'123',
244-
{ expiresAt: new Date(), numAdults: 0, numChildren: 0 }
254+
{
255+
expiresAt: new Date(),
256+
numAdults: 0,
257+
numChildren: 0,
258+
housingType: 'RENTAL',
259+
housingTypeDescription: null,
260+
landlord: null,
261+
lastUpdatedAt: new Date(),
262+
housingReference:
263+
factory.applicationProfileHousingReference.build(),
264+
}
245265
)
246266

247267
expect(result).toEqual({ ok: false, err: 'unknown' })
@@ -251,25 +271,28 @@ describe('leasing-adapter', () => {
251271
nock(config.tenantsLeasesService.url)
252272
.post('/contacts/123/application-profile')
253273
.reply(200, {
254-
content: {
255-
contactCode: '1234',
256-
numChildren: 0,
257-
numAdults: 0,
258-
expiresAt: null,
259-
id: 1,
260-
createdAt: new Date(),
261-
},
274+
content: factory.applicationProfile.build(),
262275
})
263276

264277
const result =
265278
await leasingAdapter.createOrUpdateApplicationProfileByContactCode(
266279
'123',
267-
{ expiresAt: new Date(), numAdults: 0, numChildren: 0 }
280+
{
281+
expiresAt: new Date(),
282+
numAdults: 0,
283+
numChildren: 0,
284+
housingType: 'RENTAL',
285+
housingTypeDescription: null,
286+
landlord: null,
287+
lastUpdatedAt: new Date(),
288+
housingReference:
289+
factory.applicationProfileHousingReference.build(),
290+
}
268291
)
269292

270293
assert(result.ok)
271294
expect(() =>
272-
leasing.CreateOrUpdateApplicationProfileResponseDataSchema.parse(
295+
leasing.v1.CreateOrUpdateApplicationProfileResponseDataSchema.parse(
273296
result.data
274297
)
275298
).not.toThrow()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import KoaRouter from '@koa/router'
2+
import dayjs from 'dayjs'
3+
import { generateRouteMetadata } from 'onecore-utilities'
4+
import { z } from 'zod'
5+
6+
import * as leasingAdapter from '../../adapters/leasing-adapter'
7+
import { parseRequestBody } from '../../middlewares/parse-request-body'
8+
import { schemas } from './schemas'
9+
10+
// TODO: Remove this once all routes are migrated to the new application
11+
// profile (with housing references)
12+
export const routes = (router: KoaRouter) => {
13+
router.post(
14+
'(.*)/contacts/:contactCode/application-profile',
15+
parseRequestBody(
16+
schemas.client.applicationProfile.UpdateApplicationProfileRequestParamsOld
17+
),
18+
async (ctx) => {
19+
const metadata = generateRouteMetadata(ctx)
20+
// TODO: Something wrong with parseRequestBody types.
21+
// Body should be inferred from middleware
22+
const body = ctx.request.body as z.infer<
23+
typeof schemas.client.applicationProfile.UpdateApplicationProfileRequestParamsOld
24+
>
25+
26+
const getApplicationProfile =
27+
await leasingAdapter.getApplicationProfileByContactCode(
28+
ctx.params.contactCode
29+
)
30+
31+
if (
32+
!getApplicationProfile.ok &&
33+
getApplicationProfile.err !== 'not-found'
34+
) {
35+
ctx.status = 500
36+
ctx.body = { error: 'unknown', ...metadata }
37+
return
38+
}
39+
40+
const expiresAt = dayjs(new Date()).add(6, 'months').toDate()
41+
const housingReferenceParams: leasingAdapter.CreateOrUpdateApplicationProfileRequestParams['housingReference'] =
42+
{
43+
expiresAt,
44+
email: body.housingReference?.email ?? null,
45+
phone: body.housingReference?.phone ?? null,
46+
comment: null,
47+
reasonRejected: null,
48+
reviewStatus: 'PENDING',
49+
reviewedAt: null,
50+
reviewedBy: null,
51+
}
52+
53+
const createOrUpdate =
54+
await leasingAdapter.createOrUpdateApplicationProfileByContactCode(
55+
ctx.params.contactCode,
56+
{
57+
housingTypeDescription: body.housingTypeDescription ?? null,
58+
expiresAt,
59+
housingReference: housingReferenceParams,
60+
lastUpdatedAt: new Date(),
61+
housingType: 'OTHER',
62+
landlord: body.landlord ?? null,
63+
numAdults: body.numAdults,
64+
numChildren: body.numChildren,
65+
}
66+
)
67+
68+
if (!createOrUpdate.ok) {
69+
ctx.status = 500
70+
ctx.body = { error: 'unknown', ...metadata }
71+
return
72+
}
73+
74+
ctx.status = createOrUpdate.statusCode ?? 200
75+
ctx.body = {
76+
content: createOrUpdate.data satisfies z.infer<
77+
typeof schemas.client.applicationProfile.UpdateApplicationProfileResponseData
78+
>,
79+
...metadata,
80+
}
81+
}
82+
)
83+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import dayjs from 'dayjs'
2+
import { z } from 'zod'
3+
4+
import { schemas } from '../schemas'
5+
import * as leasingAdapter from '../../../adapters/leasing-adapter'
6+
7+
type UpdateAdminApplicationProfileRequestParams = z.infer<
8+
typeof schemas.admin.applicationProfile.UpdateApplicationProfileRequestParams
9+
>
10+
11+
export function makeAdminApplicationProfileRequestParams(
12+
body: UpdateAdminApplicationProfileRequestParams,
13+
existingProfile?: leasingAdapter.GetApplicationProfileResponseData
14+
): leasingAdapter.CreateOrUpdateApplicationProfileRequestParams {
15+
const now = new Date()
16+
const expiresAt = dayjs(now).add(6, 'months').toDate()
17+
18+
if (!existingProfile) {
19+
return {
20+
...body,
21+
lastUpdatedAt: null,
22+
expiresAt: null,
23+
housingReference: {
24+
...body.housingReference,
25+
expiresAt: null,
26+
reviewedAt:
27+
body.housingReference.reviewStatus === 'PENDING' ? null : now,
28+
},
29+
}
30+
}
31+
32+
const diffType = getDiffType(body, existingProfile)
33+
if (diffType === 'neither') {
34+
return {
35+
...body,
36+
lastUpdatedAt: existingProfile.lastUpdatedAt,
37+
expiresAt: existingProfile.expiresAt,
38+
housingReference: {
39+
...body.housingReference,
40+
reviewedAt: existingProfile.housingReference.reviewedAt,
41+
expiresAt: existingProfile.housingReference.expiresAt,
42+
},
43+
}
44+
} else if (diffType === 'profile') {
45+
return {
46+
...body,
47+
expiresAt,
48+
lastUpdatedAt: now,
49+
housingReference: {
50+
...body.housingReference,
51+
reviewedAt: existingProfile.housingReference.reviewedAt,
52+
expiresAt: existingProfile.housingReference.expiresAt,
53+
},
54+
}
55+
} else if (diffType === 'review') {
56+
return {
57+
...body,
58+
lastUpdatedAt: existingProfile.lastUpdatedAt,
59+
expiresAt: existingProfile.expiresAt,
60+
housingReference: {
61+
...body.housingReference,
62+
expiresAt,
63+
reviewedAt: now,
64+
},
65+
}
66+
} else {
67+
return {
68+
...body,
69+
expiresAt,
70+
lastUpdatedAt: now,
71+
housingReference: {
72+
...body.housingReference,
73+
expiresAt,
74+
reviewedAt: now,
75+
},
76+
}
77+
}
78+
}
79+
80+
function getDiffType(
81+
params: UpdateAdminApplicationProfileRequestParams,
82+
existing: leasingAdapter.GetApplicationProfileResponseData
83+
): 'profile' | 'review' | 'both' | 'neither' {
84+
const { housingReference: incomingHousingReference, ...incomingProfile } =
85+
params
86+
const { housingReference: existingHousingReference, ...existingProfile } =
87+
schemas.admin.applicationProfile.UpdateApplicationProfileRequestParams.parse(
88+
existing
89+
)
90+
91+
const reviewed =
92+
incomingHousingReference.reviewStatus !==
93+
existingHousingReference.reviewStatus
94+
95+
const profileUpdate =
96+
JSON.stringify(incomingProfile) !== JSON.stringify(existingProfile)
97+
98+
if (reviewed && profileUpdate) {
99+
return 'both'
100+
} else if (reviewed) {
101+
return 'review'
102+
} else if (profileUpdate) {
103+
return 'profile'
104+
} else {
105+
return 'neither'
106+
}
107+
}

0 commit comments

Comments
 (0)