Skip to content

Commit

Permalink
fix: fallback and refresh translations (#4105)
Browse files Browse the repository at this point in the history
* fix: fallback and refresh translations

* fix: commenting

* fix: remove logging

* fix: updated translation logic

* fix: add contentUpdatedAt field

* fix: seeding updates

* fix: add testing coverage

* fix: testing tweaks

* fix: context for new field

* fix: pr review cleanup
  • Loading branch information
ColinBuyck authored Jun 13, 2024
1 parent 986c835 commit 5c2e1d1
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 3 deletions.
2 changes: 2 additions & 0 deletions api/prisma/migrations/12_add_content_updated_at/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Add field to capture most recent listing content update time
ALTER TABLE "listings" ADD COLUMN "content_updated_at" TIMESTAMPTZ(6);
1 change: 1 addition & 0 deletions api/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -508,6 +508,7 @@ model Listings {
isWaitlistOpen Boolean? @map("is_waitlist_open")
waitlistOpenSpots Int? @map("waitlist_open_spots")
customMapPin Boolean? @map("custom_map_pin")
contentUpdatedAt DateTime? @map("content_updated_at") @db.Timestamptz(6)
publishedAt DateTime? @map("published_at") @db.Timestamptz(6)
closedAt DateTime? @map("closed_at") @db.Timestamptz(6)
afsLastRunAt DateTime? @default(dbgenerated("'1970-01-01 00:00:00-07'::timestamp with time zone")) @map("afs_last_run_at") @db.Timestamptz(6)
Expand Down
6 changes: 6 additions & 0 deletions api/prisma/seed-staging.ts
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ export const stagingSeed = async (
isWaitlistOpen: false,
waitlistOpenSpots: null,
customMapPin: false,
contentUpdatedAt: new Date(),
publishedAt: new Date(),
listingsBuildingAddress: {
create: whiteHouse,
Expand Down Expand Up @@ -414,6 +415,7 @@ export const stagingSeed = async (
isWaitlistOpen: false,
waitlistOpenSpots: null,
customMapPin: false,
contentUpdatedAt: new Date(),
publishedAt: new Date(),
listingsApplicationPickUpAddress: undefined,
listingsApplicationDropOffAddress: undefined,
Expand Down Expand Up @@ -561,6 +563,7 @@ export const stagingSeed = async (
isWaitlistOpen: false,
waitlistOpenSpots: null,
customMapPin: false,
contentUpdatedAt: new Date(),
publishedAt: new Date(),
listingsBuildingAddress: {
create: whiteHouse,
Expand Down Expand Up @@ -678,6 +681,7 @@ export const stagingSeed = async (
isWaitlistOpen: false,
waitlistOpenSpots: null,
customMapPin: false,
contentUpdatedAt: dayjs(new Date()).subtract(1, 'days').toDate(),
publishedAt: dayjs(new Date()).subtract(3, 'days').toDate(),
closedAt: dayjs(new Date()).subtract(1, 'days').toDate(),
listingsApplicationPickUpAddress: undefined,
Expand Down Expand Up @@ -781,6 +785,7 @@ export const stagingSeed = async (
isWaitlistOpen: true,
waitlistOpenSpots: 6,
customMapPin: false,
contentUpdatedAt: new Date(),
publishedAt: new Date(),
listingsApplicationPickUpAddress: undefined,
listingsApplicationDropOffAddress: undefined,
Expand Down Expand Up @@ -870,6 +875,7 @@ export const stagingSeed = async (
isWaitlistOpen: false,
waitlistOpenSpots: null,
customMapPin: false,
contentUpdatedAt: new Date(),
publishedAt: new Date(),
listingsApplicationPickUpAddress: undefined,
listingsApplicationDropOffAddress: undefined,
Expand Down
8 changes: 8 additions & 0 deletions api/src/dtos/listings/listing.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,14 @@ class Listing extends AbstractDTO {
@ApiPropertyOptional()
customMapPin?: boolean;

//Used to refresh translations and communicate recent changes to admin users
//should be revisited after translations refactoring to see if its still useful
@Expose()
@IsDate({ groups: [ValidationsGroupsEnum.default] })
@Type(() => Date)
@ApiPropertyOptional()
contentUpdatedAt?: Date;

@Expose()
@IsDate({ groups: [ValidationsGroupsEnum.default] })
@Type(() => Date)
Expand Down
2 changes: 2 additions & 0 deletions api/src/services/listing.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,6 +893,7 @@ export class ListingService implements OnModuleInit {
}
: undefined,
requestedChangesUser: undefined,
contentUpdatedAt: new Date(),
},
});

Expand Down Expand Up @@ -1363,6 +1364,7 @@ export class ListingService implements OnModuleInit {
})),
}
: undefined,
contentUpdatedAt: new Date(),
publishedAt:
storedListing.status !== ListingsStatusEnum.active &&
dto.status === ListingsStatusEnum.active
Expand Down
36 changes: 33 additions & 3 deletions api/src/services/translation.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,9 @@ export class TranslationService {

if (translatedValue) {
[...Object.keys(cleanedPaths).values()].forEach((path, index) => {
lodash.set(listing, path, translatedValue[0][index]);
if (translatedValue[0][index]) {
lodash.set(listing, path, translatedValue[0][index]);
}
});
}

Expand All @@ -192,9 +194,37 @@ export class TranslationService {
listing: Listing,
language: LanguagesEnum,
) {
return this.prisma.generatedListingTranslations.findFirst({
where: { listingId: listing.id, language: language },
const existingTranslations =
await this.prisma.generatedListingTranslations.findFirst({
where: {
listingId: listing.id,
language: language,
},
});

//determine when listing or associated preferences most recently changed
let mostRecentUpdate = listing.contentUpdatedAt;
listing.listingMultiselectQuestions?.forEach((multiselectObj) => {
const multiselectUpdatedAt =
multiselectObj.multiselectQuestions?.updatedAt;
if (mostRecentUpdate < multiselectUpdatedAt) {
mostRecentUpdate = multiselectUpdatedAt;
}
});
//refresh translations if application content changed since translation creation
if (
existingTranslations &&
existingTranslations.createdAt < mostRecentUpdate
) {
await this.prisma.generatedListingTranslations.delete({
where: {
id: existingTranslations.id,
},
});
return undefined;
}

return existingTranslations;
}

private async persistNewTranslatedValues(
Expand Down
4 changes: 4 additions & 0 deletions api/test/unit/services/listing.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1771,6 +1771,7 @@ describe('Testing listing service', () => {
},
data: {
name: 'example listing name',
contentUpdatedAt: expect.anything(),
depositMin: '5',
assets: {
create: [
Expand Down Expand Up @@ -1870,6 +1871,7 @@ describe('Testing listing service', () => {
},
data: {
...val,
contentUpdatedAt: expect.anything(),
assets: {
create: [exampleAsset],
},
Expand Down Expand Up @@ -2306,6 +2308,7 @@ describe('Testing listing service', () => {
},
data: {
name: 'example listing name',
contentUpdatedAt: expect.anything(),
depositMin: '5',
assets: [
{
Expand Down Expand Up @@ -2430,6 +2433,7 @@ describe('Testing listing service', () => {
...val,
id: undefined,
publishedAt: expect.anything(),
contentUpdatedAt: expect.anything(),
assets: [exampleAsset],
applicationMethods: {
create: [
Expand Down
28 changes: 28 additions & 0 deletions api/test/unit/services/translation.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import { TranslationService } from '../../../src/services/translation.service';
import { PrismaService } from '../../../src/services/prisma.service';
import { Test, TestingModule } from '@nestjs/testing';
import { GoogleTranslateService } from '../../../src/services/google-translate.service';
import dayjs from 'dayjs';

const mockListing = (): Listing => {
const basicListing = {
id: 'id 1',
createdAt: new Date(),
updatedAt: new Date(),
contentUpdatedAt: new Date(),
name: 'listing 1',
status: ListingsStatusEnum.active,
displayWaitlistSize: true,
Expand Down Expand Up @@ -210,6 +212,31 @@ describe('Testing translations service', () => {
validateTranslatedFields(result);
});

it('Should fetch translations and translate listing if db translations outdated', async () => {
googleTranslateServiceMock.fetch.mockResolvedValueOnce([translatedStrings]);
prisma.generatedListingTranslations.findFirst = jest
.fn()
.mockResolvedValue({
createdAt: dayjs(new Date()).subtract(1, 'days').toDate(),
id: randomUUID(),
translations: [translatedStrings],
});
prisma.generatedListingTranslations.delete = jest
.fn()
.mockResolvedValue(null);

const result = await service.translateListing(
mockListing() as Listing,
LanguagesEnum.es,
);
expect(googleTranslateServiceMock.fetch).toHaveBeenCalledTimes(1);
expect(prisma.generatedListingTranslations.findFirst).toHaveBeenCalledTimes(
1,
);
expect(prisma.generatedListingTranslations.delete).toHaveBeenCalledTimes(1);
validateTranslatedFields(result);
});

it('Should fetch translations from db and translate listing', async () => {
prisma.generatedListingTranslations.findFirst = jest
.fn()
Expand Down Expand Up @@ -238,6 +265,7 @@ describe('Testing translations service', () => {
expect(prisma.translations.findFirst).toBeCalledTimes(1);
expect(result).toEqual(nullJurisdiction);
});

it('Should get merged translations for jurisdiction in english', async () => {
const nullJurisdiction = {
value: 'null jurisdiction',
Expand Down
9 changes: 9 additions & 0 deletions shared-helpers/src/types/backend-swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2945,6 +2945,9 @@ export interface Listing {
/** */
customMapPin?: boolean

/** */
contentUpdatedAt?: Date

/** */
publishedAt?: Date

Expand Down Expand Up @@ -3449,6 +3452,9 @@ export interface ListingCreate {
/** */
customMapPin?: boolean

/** */
contentUpdatedAt?: Date

/** */
lastApplicationUpdateAt?: Date

Expand Down Expand Up @@ -3697,6 +3703,9 @@ export interface ListingUpdate {
/** */
customMapPin?: boolean

/** */
contentUpdatedAt?: Date

/** */
lastApplicationUpdateAt?: Date

Expand Down

0 comments on commit 5c2e1d1

Please sign in to comment.