-
Notifications
You must be signed in to change notification settings - Fork 3.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(dal,api): strict null check and type fixes #2893
Conversation
NV-1753 The repository function `findById` has wrong return type
Why? (Context)The repository function What?Revisit all the repositories. Definition of DoneThe return type should be like |
@@ -20,7 +20,11 @@ export class SwitchEnvironment { | |||
} | |||
|
|||
const member = await this.memberRepository.findMemberByUserId(command.organizationId, command.userId); | |||
if (!member) throw new NotFoundException('Member is not found'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mainly in a lot of places I've added these kind of checks, because the repository function now returns Entity | null
@@ -18,7 +18,7 @@ export class PasswordResetRequest { | |||
async execute(command: PasswordResetRequestCommand): Promise<{ success: boolean }> { | |||
const email = normalizeEmail(command.email); | |||
const foundUser = await this.userRepository.findByEmail(email); | |||
if (foundUser) { | |||
if (foundUser && foundUser.email) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a reset use-case and user.email
should exist
const config: { | ||
applicationId: string; | ||
clientId: string; | ||
secretKey: string; | ||
} = { applicationId: credentials.applicationId, clientId: credentials.clientId, secretKey: credentials.secretKey }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these were actually not used in the SlackProvider
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot.
@@ -9,5 +9,5 @@ export interface IChatHandler { | |||
} | |||
|
|||
export interface IChatFactory { | |||
getHandler(integration: IntegrationEntity): IChatHandler; | |||
getHandler(integration: IntegrationEntity): IChatHandler | null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
email, push, sms, chat factories might not return the handler, so this is the correct return type
if (!currentJob) throw new ApiException('Digest job is not found'); | ||
|
||
const amount = | ||
typeof currentJob?.digest.amount === 'number' | ||
? currentJob?.digest.amount | ||
: parseInt(currentJob.digest.amount, 10); | ||
typeof currentJob.digest?.amount === 'number' | ||
? currentJob.digest.amount | ||
: parseInt(currentJob.digest?.amount ?? '0', 10); | ||
const earliest = sub(new Date(currentJob.createdAt), { | ||
[currentJob.digest.unit]: amount, | ||
[currentJob.digest?.unit ?? DigestUnitEnum.MINUTES]: amount, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there were multiple TS errors, I've added default values, hope this solution is correct ;D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't do defaults here as would be confusing for the user if for any reason or bug they have selected hours or days and the digest gets executed based in minutes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@p-fernandez so what's your suggestion? I can add the // @ts-ignore
comments...
I'll revert it because it works with NaN
and undefined
preserving the date:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should throw an exception to be honest. But it is not responsibility of this PR.
Please add the ignore comment and a TODO, please. 🙏🏻
if (!currentTrigger) { | ||
this.createExecutionDetails.execute( | ||
CreateExecutionDetailsCommand.create({ | ||
...CreateExecutionDetailsCommand.getDetailsFromJob(currentJob), | ||
detail: DetailEnum.DIGEST_TRIGGERED_EVENTS, | ||
source: ExecutionDetailsSourceEnum.INTERNAL, | ||
status: ExecutionDetailsStatusEnum.FAILED, | ||
isTest: false, | ||
isRetry: false, | ||
}) | ||
); | ||
throw new ApiException('Trigger job is not found'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this should not be possible, but in the case
@@ -90,7 +90,7 @@ export class UpdateLayoutUseCase { | |||
private mapToEntity(layout: LayoutDto): LayoutEntity { | |||
return { | |||
...layout, | |||
_id: layout._id, | |||
_id: layout._id as string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
didn't want to fix this issue here because it requires a lot of changes
@@ -37,7 +37,7 @@ export class CreateMessageTemplate { | |||
actor: command.actor, | |||
}); | |||
|
|||
item = await this.messageTemplateRepository.findById(item._id); | |||
item = (await this.messageTemplateRepository.findById(item._id)) as MessageTemplateEntity; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the message template is created above
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const mailObject: any = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also wasn't able to fix the TS issues here
@@ -77,7 +77,7 @@ export class BaseRepository<T_DBModel, T_MappedEntity> { | |||
}) | |||
.batchSize(batchSize) | |||
.cursor()) { | |||
yield this.mapEntities(doc); | |||
yield this.mapEntity(doc); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the doc is a single entity
@@ -7,7 +7,7 @@ import { MSTeamsHandler } from './handlers/msteams.handler'; | |||
export class ChatFactory implements IChatFactory { | |||
handlers: IChatHandler[] = [new SlackHandler(), new DiscordHandler(), new MSTeamsHandler()]; | |||
|
|||
getHandler(integration: IntegrationEntity): IChatHandler { | |||
getHandler(integration: IntegrationEntity) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the reason behind removing some of the return types?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the functions are defined in the interface IChatFactory
, so you don't have to redefine them here
const config: { | ||
applicationId: string; | ||
clientId: string; | ||
secretKey: string; | ||
} = { applicationId: credentials.applicationId, clientId: credentials.clientId, secretKey: credentials.secretKey }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good spot.
@@ -15,7 +15,7 @@ export abstract class BaseChatHandler implements IChatHandler { | |||
|
|||
async send(chatContent: IChatOptions) { | |||
if (process.env.NODE_ENV === 'test') { | |||
return null; | |||
return {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I particularly do not like this. Returning an empty object I think it can cause a lot of problems and doesn't make much sense, to be honest.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If not an empty object then I'll have to add null
to the return type and do additional checks in the code for that null, and null is only required for the tests, so to me it feels odd.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We are in different hills 😂 .
But having to return anything I'd go for undefined
for the same reasons I mentioned in a different comment. Also because return;
returns undefined.
Your PR, your choice, though. 👍🏻
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this empty object is fine for now, it's only returned in testing env...
apps/api/src/app/events/usecases/send-message/digest/digest.usecase.ts
Outdated
Show resolved
Hide resolved
…-1753-dal-and-api-strict-null-check
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🌟
Big big work. Thank you for that.
@@ -7,7 +7,7 @@ export class CreateLog { | |||
constructor(private logRepository: LogRepository) {} | |||
|
|||
async execute(command: CreateLogCommand): Promise<LogEntity> { | |||
let rawData: string = null; | |||
let rawData: string | null = null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I truly thought TS would interpret it as undefined
type. Wow, my bad. 😓
I know I am insisting a bit on the issue in a different commands but not keen on using null
. It is not directly passing to JSON.stringify
but JSON.stringify(null)
becomes 'null'
while JSON.stringify(undefined)
becomes undefined
. In this case it would be in the command.raw
but if arrives to rawData
we can have problems thinking that could get an empty value while it could hold null stringified.
Just wanted to highlight those risks of using null
because they can lead to unexpected bugs. So apologies for the repetition. 🙏🏻
if (!currentJob) throw new ApiException('Digest job is not found'); | ||
|
||
const amount = | ||
typeof currentJob?.digest.amount === 'number' | ||
? currentJob?.digest.amount | ||
: parseInt(currentJob.digest.amount, 10); | ||
typeof currentJob.digest?.amount === 'number' | ||
? currentJob.digest.amount | ||
: parseInt(currentJob.digest?.amount ?? '0', 10); | ||
const earliest = sub(new Date(currentJob.createdAt), { | ||
[currentJob.digest.unit]: amount, | ||
[currentJob.digest?.unit ?? DigestUnitEnum.MINUTES]: amount, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should throw an exception to be honest. But it is not responsibility of this PR.
Please add the ignore comment and a TODO, please. 🙏🏻
@@ -19,5 +19,5 @@ export class CreateIntegrationCommand extends EnvironmentCommand { | |||
check: boolean; | |||
|
|||
@IsOptional() | |||
userId?: string; | |||
userId: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then let's remove the isOptional
. 💪🏻
…-1753-dal-and-api-strict-null-check
…-1753-dal-and-api-strict-null-check
What change does this PR introduce?
This is the second part, the previous is #2890.
Here I've enabled the strict null check flag in the TS build config for DAL and API, resulting in many null/undefined errors in the API app.
Solved ~300 TS issues.
Some of these errors were visible in the Sentry, ex. here: https://novu-r9.sentry.io/issues/3841226005/?project=6248811&query=is%3Aunresolved&referrer=issue-stream
Why was this change needed?
Other information (Screenshots)