Skip to content

Commit ba56331

Browse files
authored
Merge branch 'main' into chore/tags
2 parents ca7d7db + 3f91ee6 commit ba56331

File tree

98 files changed

+1802
-607
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

98 files changed

+1802
-607
lines changed

nest-cli.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
"assets": [
88
{
99
"include": "mail/templates/**"
10-
}
10+
},
11+
{ "include": "i18n/**/*", "watchAssets": true }
1112
]
1213
}
1314
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"meilisearch": "^0.50.0",
8585
"minio": "^8.0.6",
8686
"nanoid": "^5.1.6",
87+
"nestjs-i18n": "^10.5.1",
8788
"nestjs-otel": "^7.0.1",
8889
"nodemailer": "^6.10.1",
8990
"openai": "^4.104.0",

pnpm-lock.yaml

Lines changed: 51 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/api-key/api-key.controller.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
UpdateAPIKeyDto,
77
} from 'omniboxd/api-key/api-key.dto';
88
import {
9-
BadRequestException,
109
Body,
1110
Controller,
1211
Delete,
@@ -16,7 +15,10 @@ import {
1615
Post,
1716
Put,
1817
Query,
18+
HttpStatus,
1919
} from '@nestjs/common';
20+
import { AppException } from 'omniboxd/common/exceptions/app.exception';
21+
import { I18n, I18nContext } from 'nestjs-i18n';
2022

2123
@Controller('api/v1/api-keys')
2224
export class APIKeyController {
@@ -31,12 +33,16 @@ export class APIKeyController {
3133

3234
@Get()
3335
async findAll(
36+
@I18n() i18n: I18nContext,
3437
@Query('user_id') userId?: string,
3538
@Query('namespace_id') namespaceId?: string,
3639
): Promise<APIKeyResponseDto[]> {
3740
if (!userId && !namespaceId) {
38-
throw new BadRequestException(
39-
'Either user_id or namespace_id must be provided',
41+
const message = i18n.t('apikey.errors.userOrNamespaceRequired');
42+
throw new AppException(
43+
message,
44+
'USER_OR_NAMESPACE_REQUIRED',
45+
HttpStatus.BAD_REQUEST,
4046
);
4147
}
4248
return await this.apiKeyService.findAll(userId, namespaceId);

src/api-key/api-key.service.spec.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import { ResourcePermission } from 'omniboxd/permissions/resource-permission.enu
1515
import { CreateAPIKeyDto } from './api-key.dto';
1616
import { Applications } from 'omniboxd/applications/applications.entity';
1717
import { UserService } from 'omniboxd/user/user.service';
18+
import { I18nService } from 'nestjs-i18n';
19+
import { AppException } from 'omniboxd/common/exceptions/app.exception';
1820

1921
describe('APIKeyService', () => {
2022
let service: APIKeyService;
@@ -68,6 +70,23 @@ describe('APIKeyService', () => {
6870
find: jest.fn(),
6971
};
7072

73+
const mockI18nService = {
74+
t: jest.fn((key: string, options?: any) => {
75+
// Return mock translations for test purposes
76+
const translations: Record<string, string> = {
77+
'apikey.errors.noPermissionForNamespace': 'User {{userId}} does not have permission to namespace {{namespaceId}}',
78+
'apikey.errors.noWritePermission': 'User {{userId}} does not have write permission to resource {{resourceId}} in namespace {{namespaceId}}',
79+
};
80+
let translation = translations[key] || key;
81+
if (options?.args) {
82+
Object.entries(options.args).forEach(([param, value]) => {
83+
translation = translation.replace(`{{${param}}}`, String(value));
84+
});
85+
}
86+
return translation;
87+
}),
88+
};
89+
7190
const module: TestingModule = await Test.createTestingModule({
7291
providers: [
7392
APIKeyService,
@@ -91,6 +110,10 @@ describe('APIKeyService', () => {
91110
provide: UserService,
92111
useValue: mockUserService,
93112
},
113+
{
114+
provide: I18nService,
115+
useValue: mockI18nService,
116+
},
94117
],
95118
}).compile();
96119

@@ -161,12 +184,12 @@ describe('APIKeyService', () => {
161184
});
162185
});
163186

164-
it('should throw ForbiddenException when user is not a namespace member', async () => {
187+
it('should throw AppException when user is not a namespace member', async () => {
165188
// Mock namespace membership check to return null (not a member)
166189
namespacesService.getMemberByUserId.mockResolvedValue(null);
167190

168191
await expect(service.create(createApiKeyDto)).rejects.toThrow(
169-
ForbiddenException,
192+
AppException,
170193
);
171194
await expect(service.create(createApiKeyDto)).rejects.toThrow(
172195
'User test-user-id does not have permission to namespace test-namespace-id',
@@ -181,7 +204,7 @@ describe('APIKeyService', () => {
181204
expect(apiKeyRepository.create).not.toHaveBeenCalled();
182205
});
183206

184-
it('should throw ForbiddenException when user lacks write permission to resource', async () => {
207+
it('should throw AppException when user lacks write permission to resource', async () => {
185208
// Mock namespace membership check
186209
namespacesService.getMemberByUserId.mockResolvedValue({
187210
id: 1,
@@ -195,7 +218,7 @@ describe('APIKeyService', () => {
195218
permissionsService.userHasPermission.mockResolvedValue(false);
196219

197220
await expect(service.create(createApiKeyDto)).rejects.toThrow(
198-
ForbiddenException,
221+
AppException,
199222
);
200223
await expect(service.create(createApiKeyDto)).rejects.toThrow(
201224
'User test-user-id does not have write permission to resource test-resource-id in namespace test-namespace-id',

0 commit comments

Comments
 (0)