-
Notifications
You must be signed in to change notification settings - Fork 83
feat: add a delete notifications endpoint #2598
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import { deleteSubscriptionsDtoBuilder } from '@/routes/notifications/v2/entities/__tests__/delete-subscriptions.dto.builder'; | ||
import { DeleteSubscriptionsDtoSchema } from '@/domain/notifications/v2/entities/delete-subscriptions.dto.entity'; | ||
import { faker } from '@faker-js/faker'; | ||
import { getAddress } from 'viem'; | ||
|
||
describe('DeleteSubscriptionsDtoSchema', () => { | ||
it('should validate a valid DeleteSubscriptionsDto', () => { | ||
const dto = deleteSubscriptionsDtoBuilder().build(); | ||
|
||
const result = DeleteSubscriptionsDtoSchema.safeParse(dto); | ||
|
||
expect(result.success).toBe(true); | ||
}); | ||
|
||
it('should require safes', () => { | ||
const dto = deleteSubscriptionsDtoBuilder().build(); | ||
// @ts-expect-error test missing property | ||
delete dto.safes; | ||
|
||
const result = DeleteSubscriptionsDtoSchema.safeParse(dto); | ||
|
||
expect(!result.success && result.error.issues).toStrictEqual([ | ||
{ | ||
code: 'invalid_type', | ||
expected: 'array', | ||
message: 'Required', | ||
path: ['safes'], | ||
received: 'undefined', | ||
}, | ||
]); | ||
}); | ||
|
||
it.each([ | ||
['chainId' as const, 'string'], | ||
['address' as const, 'string'], | ||
])('should require safes[number].%s', (key, expected) => { | ||
const dto = deleteSubscriptionsDtoBuilder() | ||
.with('safes', [ | ||
{ | ||
chainId: faker.string.numeric(), | ||
address: getAddress(faker.finance.ethereumAddress()), | ||
}, | ||
]) | ||
.build(); | ||
|
||
delete dto.safes[0][key]; | ||
|
||
const result = DeleteSubscriptionsDtoSchema.safeParse(dto); | ||
|
||
expect(!result.success && result.error.issues).toStrictEqual([ | ||
{ | ||
code: 'invalid_type', | ||
expected, | ||
message: 'Required', | ||
path: ['safes', 0, key], | ||
received: 'undefined', | ||
}, | ||
]); | ||
}); | ||
|
||
it('should checksum safes[number].address', () => { | ||
const nonChecksum = faker.finance.ethereumAddress().toLowerCase(); | ||
const dto = deleteSubscriptionsDtoBuilder() | ||
.with('safes', [ | ||
{ | ||
chainId: faker.string.numeric(), | ||
address: nonChecksum as `0x${string}`, | ||
}, | ||
]) | ||
.build(); | ||
|
||
const result = DeleteSubscriptionsDtoSchema.safeParse(dto); | ||
|
||
expect(result.success && result.data.safes[0].address).toBe( | ||
getAddress(nonChecksum), | ||
); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { AddressSchema } from '@/validation/entities/schemas/address.schema'; | ||
import { ApiProperty } from '@nestjs/swagger'; | ||
import { z } from 'zod'; | ||
|
||
export const DeleteSubscriptionsDtoSafesSchema = z.object({ | ||
chainId: z.string(), | ||
address: AddressSchema, | ||
}); | ||
|
||
export const DeleteSubscriptionsDtoSchema = z.object({ | ||
safes: z.array(DeleteSubscriptionsDtoSafesSchema), | ||
}); | ||
|
||
export class DeleteSubscriptionsSafesDto | ||
implements z.infer<typeof DeleteSubscriptionsDtoSafesSchema> | ||
{ | ||
@ApiProperty() | ||
chainId!: string; | ||
|
||
@ApiProperty() | ||
address!: `0x${string}`; | ||
} | ||
|
||
export class DeleteSubscriptionsDto | ||
implements z.infer<typeof DeleteSubscriptionsDtoSchema> | ||
{ | ||
@ApiProperty({ type: [DeleteSubscriptionsSafesDto] }) | ||
safes!: Array<DeleteSubscriptionsSafesDto>; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { faker } from '@faker-js/faker'; | ||
import type { IBuilder } from '@/__tests__/builder'; | ||
import { Builder } from '@/__tests__/builder'; | ||
import { getAddress } from 'viem'; | ||
import type { DeleteSubscriptionsDto } from '@/domain/notifications/v2/entities/delete-subscriptions.dto.entity'; | ||
|
||
export function deleteSubscriptionsDtoBuilder(): IBuilder<DeleteSubscriptionsDto> { | ||
return new Builder<DeleteSubscriptionsDto>().with( | ||
'safes', | ||
Array.from({ length: faker.number.int({ min: 1, max: 5 }) }, () => ({ | ||
chainId: faker.string.numeric(), | ||
address: getAddress(faker.finance.ethereumAddress()), | ||
})), | ||
); | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -38,6 +38,19 @@ export class NotificationsServiceV2 { | |||||||||||||||||||||||||||||||||
await this.notificationsRepository.deleteSubscription(args); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
async deleteSubscriptions(args: { | ||||||||||||||||||||||||||||||||||
deviceUuid: UUID; | ||||||||||||||||||||||||||||||||||
safes: Array<{ chainId: string; safeAddress: `0x${string}` }>; | ||||||||||||||||||||||||||||||||||
}): Promise<void> { | ||||||||||||||||||||||||||||||||||
for (const safe of args.safes) { | ||||||||||||||||||||||||||||||||||
await this.notificationsRepository.deleteSubscription({ | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest we avoid calling |
||||||||||||||||||||||||||||||||||
deviceUuid: args.deviceUuid, | ||||||||||||||||||||||||||||||||||
chainId: safe.chainId, | ||||||||||||||||||||||||||||||||||
safeAddress: safe.safeAddress, | ||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
Comment on lines
+45
to
+51
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider using Promise.all to concurrently delete subscriptions for improved performance if the deletion operations are independent.
Suggested change
Copilot uses AI. Check for mistakes. Positive FeedbackNegative Feedback |
||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
async deleteDevice(deviceUuid: UUID): Promise<void> { | ||||||||||||||||||||||||||||||||||
await this.notificationsRepository.deleteDevice(deviceUuid); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
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.
Nit: For more readability we can extract this
map
to aconst