Skip to content

Commit

Permalink
feat: upgrading typeorm (#3340)
Browse files Browse the repository at this point in the history
* feat: upgrading typeorm

* fix: updates for tests

* fix: updating nestjs packages, clean up per morgan

* fix: undoing nestjs bump

* fix: missed changes

* fix: minor typeorm version update

* fix: user update per sean

* fix: listings update per sean

* fix: test updates

* fix: hopefully resolves failing build on heroku

* fix: attempt 2 on fixing heroku deploy

* fix: updates

* fix: update throttling in tests

* fix: asset test fix

* fix: updates for images and multiselect

* fix: update for mfa

* fix: type fix

* fix: another type fix

* fix: update for partner test

* fix: issue with jurisdiction resolver service

* fix: test fixes

* fix: fix for mfa tests?

* fix: attempt at mfa test fix

* fix: remove listing service from afs cron job (#3428)

* fix: update for tests

* fix: fix for dup resolution

---------

Co-authored-by: Morgan Ludtke <ludtkemorgan@gmail.com>
Co-authored-by: Sean Albert <smabert@gmail.com>
Co-authored-by: Morgan Ludtke <42942267+ludtkemorgan@users.noreply.github.com>
  • Loading branch information
4 people committed May 22, 2023
1 parent e5f8aa1 commit 287e3b0
Show file tree
Hide file tree
Showing 67 changed files with 1,288 additions and 810 deletions.
7 changes: 7 additions & 0 deletions backend/core/ormDataSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DataSource } from "typeorm"
import connectionConfig from "./ormconfig"
const ormDataSource = new DataSource({
...connectionConfig,
})

export default ormDataSource
7 changes: 7 additions & 0 deletions backend/core/ormTestDataSource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DataSource } from "typeorm"
import connectionConfig from "./ormconfig.test"
const ormDataSource = new DataSource({
...connectionConfig,
})

export default ormDataSource
26 changes: 10 additions & 16 deletions backend/core/ormconfig.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { SnakeNamingStrategy } from "typeorm-naming-strategies"
import { join } from "path"
import { DataSourceOptions } from "typeorm"

// dotenv is a dev dependency, so conditionally import it (don't need it in Prod).
try {
Expand All @@ -10,19 +11,16 @@ try {
}

const defaultConnectionForEnv = {
development: {
host: "localhost",
port: 5432,
database: "bloom",
},
host: "localhost",
port: 5432,
database: "bloom",
ssl: undefined,
}

const env = process.env.NODE_ENV || "development"

// If we have a DATABASE_URL, use that
const connectionInfo = process.env.DATABASE_URL
? { url: process.env.DATABASE_URL }
: defaultConnectionForEnv[env]
? { url: process.env.DATABASE_URL, ssl: undefined }
: defaultConnectionForEnv

// Require an SSL connection to the DB in production, and allow self-signed
if (process.env.NODE_ENV === "production") {
Expand All @@ -31,7 +29,7 @@ if (process.env.NODE_ENV === "production") {

// Unfortunately, we need to use CommonJS/AMD style exports rather than ES6-style modules for this due to how
// TypeORM expects the config to be available.
export default {
const dataSourceOptions: DataSourceOptions = {
type: "postgres",
...connectionInfo,
synchronize: false,
Expand All @@ -45,15 +43,11 @@ export default {
join(__dirname, "src/**", "*.entity.{js,ts}"),
],
migrations: [join(__dirname, "src/migration", "*.{js,ts}")],
subscribers: [join(__dirname, "src/subscriber", "*.{js,ts}")],
cli: {
entitiesDir: "src/entity",
migrationsDir: "src/migration",
subscribersDir: "src/subscriber",
},
// extra: {
// ssl: {
// rejectUnauthorized: false,
// },
// },
}

export default dataSourceOptions
24 changes: 12 additions & 12 deletions backend/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./jest-e2e.json --runInBand --forceExit",
"test:e2e:local": "psql -c 'DROP DATABASE IF EXISTS bloom_test' && psql -c 'CREATE DATABASE bloom_test' && psql -d bloom_test -c 'CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";' && yarn typeorm:test migration:run && ts-node src/seeder/seed.ts --test && yarn run test:e2e",
"typeorm": "ts-node -P ./tsconfig.json -O '{\"module\":\"commonjs\"}' ./node_modules/.bin/typeorm",
"typeorm:test": "ts-node -P ./tsconfig.json -O '{\"module\":\"commonjs\"}' ./node_modules/.bin/typeorm --config ./ormconfig.test.ts",
"typeorm": "ts-node -P ./tsconfig.json $(yarn bin typeorm) -d ./ormDataSource.ts",
"typeorm:test": "ts-node -P ./tsconfig.json $(yarn bin typeorm) -d ./ormTestDataSource.ts",
"herokusetup": "node heroku.setup.js",
"heroku-postbuild": "rimraf dist && nest build && yarn run db:migration:run",
"generate:client": "ts-node scripts/generate-axios-client.ts && prettier -w types/src/backend-swagger.ts",
Expand All @@ -42,16 +42,16 @@
"@google-cloud/translate": "^6.2.6",
"@nestjs/axios": "1.0.1",
"@nestjs/cli": "^8.2.1",
"@nestjs/common": "^8.3.1",
"@nestjs/common": "9.3.7",
"@nestjs/config": "^1.2.0",
"@nestjs/core": "^8.3.1",
"@nestjs/core": "9.3.7",
"@nestjs/jwt": "^8.0.0",
"@nestjs/passport": "^8.2.1",
"@nestjs/platform-express": "^8.3.1",
"@nestjs/passport": "9.0.3",
"@nestjs/platform-express": "9.3.9",
"@nestjs/schedule": "^2.1.0",
"@nestjs/swagger": "5.2.0",
"@nestjs/throttler": "^2.0.0",
"@nestjs/typeorm": "~8.0.3",
"@nestjs/typeorm": "9.0.1",
"@types/cache-manager": "^3.4.0",
"async-retry": "^1.3.1",
"axios": "0.21.2",
Expand All @@ -71,7 +71,7 @@
"lodash": "^4.17.21",
"nanoid": "^3.1.12",
"nestjs-twilio": "^2.1.0",
"nestjs-typeorm-paginate": "^3.1.3",
"nestjs-typeorm-paginate": "4.0.2",
"newrelic": "7.5.1",
"node-polyglot": "^2.4.0",
"passport": "^0.6.0",
Expand All @@ -85,16 +85,16 @@
"swagger-ui-express": "^4.1.4",
"ts-node": "10.8.0",
"twilio": "^3.71.3",
"typeorm": "0.2.41",
"typeorm-naming-strategies": "^1.1.0",
"typeorm": "0.3.12",
"typeorm-naming-strategies": "4.1.0",
"typescript": "4.6.4",
"uuid": "^8.3.2"
"uuid": "9.0.0"
},
"devDependencies": {
"@babel/core": "^7.21.3",
"@babel/plugin-proposal-decorators": "^7.21.0",
"@nestjs/schematics": "^8.0.7",
"@nestjs/testing": "^8.3.1",
"@nestjs/testing": "9.3.9",
"@types/axios": "^0.14.0",
"@types/cookie-parser": "1.4.3",
"@types/cron": "^1.7.3",
Expand Down
16 changes: 8 additions & 8 deletions backend/core/src/ami-charts/ami-charts.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AmiChart } from "./entities/ami-chart.entity"
import { AmiChartCreateDto, AmiChartUpdateDto } from "./dto/ami-chart.dto"
import { InjectRepository } from "@nestjs/typeorm"
import { FindOneOptions, Repository } from "typeorm"
import { FindOneOptions, FindOptionsWhere, Repository } from "typeorm"
import { NotFoundException } from "@nestjs/common"
import { AmiChartListQueryParams } from "./dto/ami-chart-list-query-params"
import { assignDefined } from "../shared/utils/assign-defined"
Expand All @@ -13,18 +13,18 @@ export class AmiChartsService {
) {}

list(queryParams?: AmiChartListQueryParams): Promise<AmiChart[]> {
const whereClause: FindOptionsWhere<AmiChart> = {}
if (queryParams.jurisdictionName) {
whereClause.jurisdiction = { name: queryParams.jurisdictionName }
} else if (queryParams.jurisdictionId) {
whereClause.jurisdiction = { id: queryParams.jurisdictionId }
}
return this.repository.find({
join: {
alias: "amiChart",
leftJoinAndSelect: { jurisdiction: "amiChart.jurisdiction" },
},
where: (qb) => {
if (queryParams.jurisdictionName) {
qb.where("jurisdiction.name = :jurisdictionName", queryParams)
} else if (queryParams.jurisdictionId) {
qb.where("jurisdiction.id = :jurisdictionId", queryParams)
}
},
where: whereClause,
})
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
import { Brackets, LessThan, MoreThanOrEqual, Repository, SelectQueryBuilder } from "typeorm"
import {
Brackets,
FindOptionsWhere,
LessThan,
MoreThanOrEqual,
Not,
Raw,
Repository,
} from "typeorm"
import { Application } from "../applications/entities/application.entity"
import { Rule } from "./types/rule-enum"
import { InjectRepository } from "@nestjs/typeorm"
import { ListingRepository } from "../listings/db/listing.repository"
import { Listing } from "../listings/entities/listing.entity"
import { ApplicationFlaggedSet } from "./entities/application-flagged-set.entity"
import { FlaggedSetStatus } from "./types/flagged-set-status-enum"
import { getView } from "../applications/views/view"
import { Inject, Injectable, Logger, OnModuleInit } from "@nestjs/common"
import { Inject, Injectable, Logger, OnModuleInit, Scope } from "@nestjs/common"
import { SchedulerRegistry } from "@nestjs/schedule"
import { CronJob } from "cron"
import { ConfigService } from "@nestjs/config"
import { CronJobService } from "../shared/services/cron-job.service"
import dayjs from "dayjs"
import { ApplicationStatus } from "../applications/types/application-status-enum"
import { ListingsQueryBuilder } from "../listings/db/listing-query-builder"

const CRON_JOB_NAME = "AFS_CRON_JOB"
const CRON_CONFIG_VALUE = "AFS_PROCESSING_CRON_STRING"
@Injectable()
@Injectable({ scope: Scope.DEFAULT })
export class ApplicationFlaggedSetsCronjobService implements OnModuleInit {
constructor(
@InjectRepository(ListingRepository) private readonly listingRepository: ListingRepository,
@InjectRepository(Listing) private readonly listingRepository: Repository<Listing>,
@InjectRepository(ApplicationFlaggedSet)
private readonly afsRepository: Repository<ApplicationFlaggedSet>,
@InjectRepository(Application) private readonly applicationRepository: Repository<Application>,
@Inject(Logger) private readonly logger = new Logger(ApplicationFlaggedSetsCronjobService.name),
@Inject(Logger)
private readonly logger = new Logger(ApplicationFlaggedSetsCronjobService.name),
private schedulerRegistry: SchedulerRegistry,
private readonly config: ConfigService,
private readonly cronJobService: CronJobService
Expand Down Expand Up @@ -58,8 +68,9 @@ export class ApplicationFlaggedSetsCronjobService implements OnModuleInit {
public async process() {
this.logger.warn("running the Application flagged sets cron job")
await this.cronJobService.saveCronJobByName(CRON_JOB_NAME)
const outOfDateListings = await this.listingRepository
.createQueryBuilder("listings")
const outOfDateListings = await new ListingsQueryBuilder(
this.listingRepository.createQueryBuilder("listings")
)
.select(["listings.id", "listings.afsLastRunAt"])
.where("listings.lastApplicationUpdateAt IS NOT NULL")
.andWhere(
Expand Down Expand Up @@ -122,7 +133,7 @@ export class ApplicationFlaggedSetsCronjobService implements OnModuleInit {
.where(`afs.listing_id = :listingId`, { listingId: application.listingId })
.getMany()

afses = afses.filter((afs) => afs.applications.map((app) => app.id).includes(application.id))
afses = afses.filter((afs) => afs.applications.some((app) => app.id === application.id))

const afsesToBeSaved: Array<ApplicationFlaggedSet> = []
const afsesToBeRemoved: Array<ApplicationFlaggedSet> = []
Expand Down Expand Up @@ -216,20 +227,19 @@ export class ApplicationFlaggedSetsCronjobService implements OnModuleInit {
}

private async fetchDuplicatesMatchingEmailRule(newApplication: Application) {
const whereClause: FindOptionsWhere<Application> = {
id: Not(newApplication.id),
status: ApplicationStatus.submitted,
listing: {
id: newApplication.listingId,
},
applicant: {
emailAddress: newApplication.applicant.emailAddress,
},
}
return await this.applicationRepository.find({
select: ["id"],
where: (qb: SelectQueryBuilder<Application>) => {
qb.where("Application.id != :id", {
id: newApplication.id,
})
.andWhere("Application.listing.id = :listingId", {
listingId: newApplication.listingId,
})
.andWhere("Application__applicant.emailAddress = :emailAddress", {
emailAddress: newApplication.applicant.emailAddress,
})
.andWhere("Application.status = :status", { status: "submitted" })
},
where: whereClause,
})
}

Expand Down Expand Up @@ -272,67 +282,53 @@ export class ApplicationFlaggedSetsCronjobService implements OnModuleInit {
...newApplication.householdMembers.map((householdMember) => householdMember.birthYear),
]

const whereClause: FindOptionsWhere<Application> = {
id: Not(newApplication.id),
status: ApplicationStatus.submitted,
listing: {
id: newApplication.listingId,
},
householdMembers: {
firstName: Raw(
(alias) =>
`(${alias} IN (:...firstNames) OR Application__applicant.firstName IN (:...firstNames))`,
{
firstNames,
}
),
lastName: Raw(
(alias) =>
`(${alias} IN (:...lastNames) OR Application__applicant.lastName IN (:...lastNames))`,
{
lastNames,
}
),
birthMonth: Raw(
(alias) =>
`(${alias} IN (:...birthMonths) OR Application__applicant.birthMonth IN (:...birthMonths))`,
{
birthMonths,
}
),
birthDay: Raw(
(alias) =>
`(${alias} IN (:...birthDays) OR Application__applicant.birthDay IN (:...birthDays))`,
{
birthDays,
}
),
birthYear: Raw(
(alias) =>
`(${alias} IN (:...birthYears) OR Application__applicant.birthYear IN (:...birthYears))`,
{
birthYears,
}
),
},
}
return await this.applicationRepository.find({
select: ["id"],
where: (qb: SelectQueryBuilder<Application>) => {
qb.where("Application.id != :id", {
id: newApplication.id,
})
.andWhere("Application.listing.id = :listingId", {
listingId: newApplication.listingId,
})
.andWhere("Application.status = :status", { status: "submitted" })
.andWhere(
new Brackets((subQb) => {
subQb.where("Application__householdMembers.firstName IN (:...firstNames)", {
firstNames: firstNames,
})
subQb.orWhere("Application__applicant.firstName IN (:...firstNames)", {
firstNames: firstNames,
})
})
)
.andWhere(
new Brackets((subQb) => {
subQb.where("Application__householdMembers.lastName IN (:...lastNames)", {
lastNames: lastNames,
})
subQb.orWhere("Application__applicant.lastName IN (:...lastNames)", {
lastNames: lastNames,
})
})
)
.andWhere(
new Brackets((subQb) => {
subQb.where("Application__householdMembers.birthMonth IN (:...birthMonths)", {
birthMonths: birthMonths,
})
subQb.orWhere("Application__applicant.birthMonth IN (:...birthMonths)", {
birthMonths: birthMonths,
})
})
)
.andWhere(
new Brackets((subQb) => {
subQb.where("Application__householdMembers.birthDay IN (:...birthDays)", {
birthDays: birthDays,
})
subQb.orWhere("Application__applicant.birthDay IN (:...birthDays)", {
birthDays: birthDays,
})
})
)
.andWhere(
new Brackets((subQb) => {
subQb.where("Application__householdMembers.birthYear IN (:...birthYears)", {
birthYears: birthYears,
})
subQb.orWhere("Application__applicant.birthYear IN (:...birthYears)", {
birthYears: birthYears,
})
})
)
},
where: whereClause,
})
}
}

0 comments on commit 287e3b0

Please sign in to comment.