Browse Source

Task/improve type safety in account subscription and user services (#8361)

Improve type safety
pull/8387/head
Kenrick Tandrian 2 days ago
committed by GitHub
parent
commit
8109fffa80
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 35
      apps/api/src/services/account/account.service.ts
  2. 29
      apps/api/src/services/subscription/subscription.service.ts
  3. 23
      apps/api/src/services/user/user.service.ts
  4. 2
      libs/common/src/lib/types/account-with-platform.type.ts

35
apps/api/src/services/account/account.service.ts

@ -73,9 +73,10 @@ export class AccountService {
accountWhereUniqueInput: Prisma.AccountWhereUniqueInput,
accountInclude: Prisma.AccountInclude
): Promise<
Account & {
| (Account & {
activities?: Order[];
}
})
| null
> {
return this.prismaService.account.findUnique({
include: accountInclude,
@ -97,7 +98,7 @@ export class AccountService {
tags?: Pick<Tag, 'id'>[];
})[];
balances?: AccountBalance[];
platform?: Platform;
platform?: Platform | null;
tags?: Tag[];
})[]
> {
@ -140,7 +141,15 @@ export class AccountService {
const startOfUtcDateOfTomorrow = getStartOfUtcDateOfTomorrow();
return accounts.map((account) => {
const result = {
const result: AccountWithBalance & {
activities?: (Order & {
SymbolProfile?: SymbolProfile;
tags?: Pick<Tag, 'id'>[];
})[];
balances?: AccountBalance[];
platform?: Platform | null;
tags?: Tag[];
} = {
...account,
balance:
// The balances are ordered by date descending, hence the first account
@ -181,7 +190,9 @@ export class AccountService {
tagIds?: string[];
userId: string;
}): Promise<Account> {
if (tagIds?.length) {
await this.tagService.validateTagIdsWithoutDraftTag({ tagIds, userId });
}
const account = await this.prismaService.account.create({
data: {
@ -248,7 +259,7 @@ export class AccountService {
return accounts.map((account) => {
const result = {
...account,
activitiesCount: account.activities.length
activitiesCount: account.activities?.length ?? 0
};
delete result.activities;
@ -296,7 +307,7 @@ export class AccountService {
totalCashBalanceInBaseCurrency = totalCashBalanceInBaseCurrency.plus(
this.exchangeRateDataService.toCurrency(
account.balance,
account.currency,
account.currency ?? currency,
currency
)
);
@ -321,7 +332,9 @@ export class AccountService {
userId: string;
where: Prisma.AccountWhereUniqueInput;
}): Promise<Account> {
if (tagIds?.length) {
await this.tagService.validateTagIdsWithoutDraftTag({ tagIds, userId });
}
const account = await this.prismaService.account.update({
data: {
@ -372,18 +385,24 @@ export class AccountService {
date?: Date;
userId: string;
}) {
const { balance, currency: currencyOfAccount } = await this.account({
const account = await this.account({
id_userId: {
userId,
id: accountId
}
});
if (!account) {
throw new Error(`Account '${accountId}' has not been found`);
}
const { balance, currency: currencyOfAccount } = account;
const amountInCurrencyOfAccount =
await this.exchangeRateDataService.toCurrencyAtDate(
amount,
currency,
currencyOfAccount,
currencyOfAccount ?? currency,
date
);

29
apps/api/src/services/subscription/subscription.service.ts

@ -86,7 +86,7 @@ export class SubscriptionService {
{
allowed_payment_method_types: ['card'],
cancel_url: `${this.configurationService.get('ROOT_URL')}/${
user.settings.settings.language
user.settings?.settings?.language ?? DEFAULT_LANGUAGE_CODE
}/account`,
client_reference_id: user.id,
line_items: [
@ -115,6 +115,10 @@ export class SubscriptionService {
stripeCheckoutSessionCreateParams
);
if (!session.url) {
throw new Error(`Stripe Checkout Session '${session.id}' is missing url`);
}
return {
sessionUrl: session.url
};
@ -170,10 +174,16 @@ export class SubscriptionService {
const durationExtension = subscriptionOffer?.durationExtension;
if (!session.client_reference_id) {
throw new Error(
`Stripe Checkout Session '${aCheckoutSessionId}' is missing client_reference_id`
);
}
try {
await this.createSubscription({
durationExtension,
price: session.amount_total / 100,
price: (session.amount_total ?? 0) / 100,
stripeCheckoutSessionId: session.id,
userId: session.client_reference_id
});
@ -214,11 +224,11 @@ export class SubscriptionService {
let offerKey: SubscriptionOfferKey = price ? 'renewal' : 'default';
if (isBefore(createdAt, parseDate('2023-01-01'))) {
if (createdAt && isBefore(createdAt, parseDate('2023-01-01')!)) {
offerKey = 'renewal-early-bird-2023';
} else if (isBefore(createdAt, parseDate('2024-01-01'))) {
} else if (createdAt && isBefore(createdAt, parseDate('2024-01-01')!)) {
offerKey = 'renewal-early-bird-2024';
} else if (isBefore(createdAt, parseDate('2025-12-01'))) {
} else if (createdAt && isBefore(createdAt, parseDate('2025-12-01')!)) {
offerKey = 'renewal-early-bird-2025';
}
@ -227,9 +237,9 @@ export class SubscriptionService {
});
return {
offer,
subscribedAt,
expiresAt: isBefore(new Date(), expiresAt) ? expiresAt : undefined,
offer: offer as SubscriptionOffer,
type: isBefore(new Date(), expiresAt)
? SubscriptionType.Premium
: SubscriptionType.Basic
@ -240,7 +250,7 @@ export class SubscriptionService {
});
return {
offer,
offer: offer as SubscriptionOffer,
type: SubscriptionType.Basic
};
}
@ -250,7 +260,7 @@ export class SubscriptionService {
key
}: {
key: SubscriptionOfferKey;
}): Promise<SubscriptionOffer> {
}): Promise<SubscriptionOffer | undefined> {
if (!this.configurationService.get('ENABLE_FEATURE_SUBSCRIPTION')) {
return undefined;
}
@ -267,7 +277,7 @@ export class SubscriptionService {
}
private getStripeLocale(
languageCode: string
languageCode?: string
): Stripe.Checkout.SessionCreateParams.Locale {
const unsupportedLanguageCodes: Record<
Exclude<
@ -286,6 +296,7 @@ export class SubscriptionService {
};
if (
languageCode &&
(SUPPORTED_LANGUAGE_CODES as readonly string[]).includes(languageCode) &&
!(languageCode in unsupportedLanguageCodes)
) {

23
apps/api/src/services/user/user.service.ts

@ -143,7 +143,7 @@ export class UserService {
}),
impersonationUserId
? this.user({ id: impersonationUserId })
: Promise.resolve<UserWithSettings>(null),
: Promise.resolve<UserWithSettings | null>(null),
this.tagService.getTagsForUser(impersonationUserId || user.id)
]);
@ -207,10 +207,8 @@ export class UserService {
activityTypes,
dateOfFirstActivity,
id,
permissions,
referralPartners,
scopes,
subscription,
systemMessage,
tags,
access: access.map((accessItem) => {
@ -223,13 +221,17 @@ export class UserService {
};
}),
accounts: accounts.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
return (a.name ?? '')
.toLowerCase()
.localeCompare((b.name ?? '').toLowerCase());
}),
permissions: permissions ?? [],
settings: {
...resolvedUserSettings,
baseCurrency: resolvedUserSettings.baseCurrency ?? DEFAULT_CURRENCY,
locale: resolvedUserSettings.locale ?? locale
}
},
subscription: subscription as IUser['subscription']
};
}
@ -436,7 +438,10 @@ export class UserService {
frequency = 6;
}
if (frequency === 1 || analytics?.activityCount % frequency === 1) {
if (
frequency === 1 ||
(analytics?.activityCount ?? 0) % frequency === 1
) {
currentPermissions.push(permissions.enableSubscriptionInterstitial);
}
@ -551,7 +556,9 @@ export class UserService {
}
user.accounts = user.accounts.sort((a, b) => {
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
return (a.name ?? '')
.toLowerCase()
.localeCompare((b.name ?? '').toLowerCase());
});
user.permissions = currentPermissions.sort();
@ -660,11 +667,13 @@ export class UserService {
});
} catch {}
if (where.id) {
try {
await this.activitiesService.deleteActivities({
userId: where.id
});
} catch {}
}
try {
await this.prismaService.settings.delete({

2
libs/common/src/lib/types/account-with-platform.type.ts

@ -1,6 +1,6 @@
import type { Account, Platform, Tag } from '@ghostfolio/prisma/browser';
export type AccountWithPlatform = Account & {
platform?: Platform;
platform?: Platform | null;
tags?: Tag[];
};

Loading…
Cancel
Save