@flusys/nestjs-localization 7.1.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/index.d.ts +3 -3
- package/controllers/index.d.ts +3 -3
- package/controllers/language.controller.d.ts +4 -4
- package/controllers/translation-key.controller.d.ts +3 -3
- package/controllers/translation.controller.d.ts +4 -4
- package/docs/index.d.ts +1 -1
- package/docs/localization-swagger.config.d.ts +1 -1
- package/dtos/index.d.ts +3 -3
- package/dtos/translation.dto.d.ts +2 -2
- package/entities/index.d.ts +6 -6
- package/entities/language.entity.d.ts +1 -1
- package/entities/translation-key.entity.d.ts +1 -1
- package/entities/translation.entity.d.ts +2 -2
- package/fesm/349.js +1179 -0
- package/fesm/692.js +1205 -0
- package/fesm/config/index.js +101 -3
- package/fesm/controllers/index.js +15 -3
- package/fesm/docs/index.js +126 -1
- package/fesm/dtos/index.js +680 -3
- package/fesm/entities/index.js +405 -8
- package/fesm/index.js +257 -7
- package/fesm/interfaces/index.js +18 -4
- package/fesm/modules/index.js +152 -1
- package/fesm/runtime.js +85 -0
- package/fesm/services/index.js +15 -5
- package/index.d.ts +7 -7
- package/interfaces/index.d.ts +4 -4
- package/interfaces/language.interface.d.ts +1 -1
- package/interfaces/localization-module-options.interface.d.ts +3 -3
- package/interfaces/translation-key.interface.d.ts +1 -1
- package/interfaces/translation.interface.d.ts +3 -3
- package/modules/index.d.ts +1 -1
- package/modules/localization.module.d.ts +2 -2
- package/package.json +25 -34
- package/services/index.d.ts +5 -5
- package/services/language.service.d.ts +6 -6
- package/services/localization-config.service.d.ts +3 -3
- package/services/localization-datasource.provider.d.ts +3 -3
- package/services/translation-key.service.d.ts +6 -6
- package/services/translation.service.d.ts +5 -5
- package/cjs/config/event-actions.js +0 -33
- package/cjs/config/index.js +0 -20
- package/cjs/config/localization.constants.js +0 -11
- package/cjs/config/message-keys.js +0 -41
- package/cjs/controllers/index.js +0 -20
- package/cjs/controllers/language.controller.js +0 -188
- package/cjs/controllers/language.controller.spec.js +0 -154
- package/cjs/controllers/translation-key.controller.js +0 -135
- package/cjs/controllers/translation-key.controller.spec.js +0 -70
- package/cjs/controllers/translation.controller.js +0 -195
- package/cjs/controllers/translation.controller.spec.js +0 -169
- package/cjs/docs/index.js +0 -11
- package/cjs/docs/localization-swagger.config.js +0 -111
- package/cjs/docs/localization-swagger.config.spec.js +0 -30
- package/cjs/dtos/index.js +0 -20
- package/cjs/dtos/language.dto.js +0 -213
- package/cjs/dtos/translation-key.dto.js +0 -215
- package/cjs/dtos/translation.dto.js +0 -269
- package/cjs/entities/index.js +0 -42
- package/cjs/entities/index.spec.js +0 -18
- package/cjs/entities/language.entity.js +0 -126
- package/cjs/entities/translation-key.entity.js +0 -126
- package/cjs/entities/translation.entity.js +0 -120
- package/cjs/index.js +0 -24
- package/cjs/interfaces/index.js +0 -21
- package/cjs/interfaces/language.interface.js +0 -4
- package/cjs/interfaces/localization-module-options.interface.js +0 -4
- package/cjs/interfaces/translation-key.interface.js +0 -4
- package/cjs/interfaces/translation.interface.js +0 -4
- package/cjs/modules/index.js +0 -18
- package/cjs/modules/localization.module.js +0 -116
- package/cjs/modules/localization.module.spec.js +0 -132
- package/cjs/services/index.js +0 -22
- package/cjs/services/language.service.js +0 -181
- package/cjs/services/language.service.spec.js +0 -256
- package/cjs/services/localization-config.service.js +0 -66
- package/cjs/services/localization-config.service.spec.js +0 -80
- package/cjs/services/localization-datasource.provider.js +0 -79
- package/cjs/services/localization-datasource.provider.spec.js +0 -85
- package/cjs/services/translation-key.service.js +0 -179
- package/cjs/services/translation-key.service.spec.js +0 -303
- package/cjs/services/translation.service.js +0 -228
- package/cjs/services/translation.service.spec.js +0 -530
- package/fesm/config/event-actions.js +0 -12
- package/fesm/config/localization.constants.js +0 -1
- package/fesm/config/message-keys.js +0 -20
- package/fesm/controllers/language.controller.js +0 -178
- package/fesm/controllers/language.controller.spec.js +0 -150
- package/fesm/controllers/translation-key.controller.js +0 -125
- package/fesm/controllers/translation-key.controller.spec.js +0 -66
- package/fesm/controllers/translation.controller.js +0 -185
- package/fesm/controllers/translation.controller.spec.js +0 -165
- package/fesm/docs/localization-swagger.config.js +0 -101
- package/fesm/docs/localization-swagger.config.spec.js +0 -26
- package/fesm/dtos/language.dto.js +0 -192
- package/fesm/dtos/translation-key.dto.js +0 -194
- package/fesm/dtos/translation.dto.js +0 -239
- package/fesm/entities/index.spec.js +0 -14
- package/fesm/entities/language.entity.js +0 -116
- package/fesm/entities/translation-key.entity.js +0 -116
- package/fesm/entities/translation.entity.js +0 -110
- package/fesm/interfaces/language.interface.js +0 -1
- package/fesm/interfaces/localization-module-options.interface.js +0 -1
- package/fesm/interfaces/translation-key.interface.js +0 -1
- package/fesm/interfaces/translation.interface.js +0 -1
- package/fesm/modules/localization.module.js +0 -106
- package/fesm/modules/localization.module.spec.js +0 -128
- package/fesm/services/language.service.js +0 -171
- package/fesm/services/language.service.spec.js +0 -252
- package/fesm/services/localization-config.service.js +0 -56
- package/fesm/services/localization-config.service.spec.js +0 -76
- package/fesm/services/localization-datasource.provider.js +0 -69
- package/fesm/services/localization-datasource.provider.spec.js +0 -81
- package/fesm/services/translation-key.service.js +0 -169
- package/fesm/services/translation-key.service.spec.js +0 -299
- package/fesm/services/translation.service.js +0 -218
- package/fesm/services/translation.service.spec.js +0 -526
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
import { Test } from '@nestjs/testing';
|
|
2
|
-
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
3
|
-
import { createMockDataSourceProvider, createMockRepository } from '@test-utils/mocks/repository.mock';
|
|
4
|
-
import { LanguageService } from './language.service';
|
|
5
|
-
import { LocalizationDataSourceProvider } from './localization-datasource.provider';
|
|
6
|
-
function buildLanguage(overrides = {}) {
|
|
7
|
-
return {
|
|
8
|
-
id: 'language-uuid-1',
|
|
9
|
-
code: 'en',
|
|
10
|
-
name: 'English',
|
|
11
|
-
nativeName: 'English',
|
|
12
|
-
direction: 'ltr',
|
|
13
|
-
isActive: true,
|
|
14
|
-
isDefault: false,
|
|
15
|
-
serial: 1,
|
|
16
|
-
isReadonly: false,
|
|
17
|
-
createdAt: new Date(),
|
|
18
|
-
updatedAt: new Date(),
|
|
19
|
-
deletedAt: null,
|
|
20
|
-
createdById: null,
|
|
21
|
-
updatedById: null,
|
|
22
|
-
deletedById: null,
|
|
23
|
-
...overrides
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
describe('LanguageService', ()=>{
|
|
27
|
-
let service;
|
|
28
|
-
let mockRepo;
|
|
29
|
-
const mockUtilsService = {
|
|
30
|
-
clearCache: jest.fn(),
|
|
31
|
-
getCacheKey: jest.fn().mockReturnValue('cache-key'),
|
|
32
|
-
trackCacheKey: jest.fn()
|
|
33
|
-
};
|
|
34
|
-
// Call history is shared across tests in this file; reset it so each cache assertion is exact.
|
|
35
|
-
beforeEach(()=>{
|
|
36
|
-
mockUtilsService.clearCache.mockClear();
|
|
37
|
-
});
|
|
38
|
-
beforeEach(async ()=>{
|
|
39
|
-
mockRepo = createMockRepository();
|
|
40
|
-
const mockDataSourceProvider = createMockDataSourceProvider(mockRepo);
|
|
41
|
-
const module = await Test.createTestingModule({
|
|
42
|
-
providers: [
|
|
43
|
-
LanguageService,
|
|
44
|
-
{
|
|
45
|
-
provide: 'CACHE_INSTANCE',
|
|
46
|
-
useValue: {}
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
provide: UtilsService,
|
|
50
|
-
useValue: mockUtilsService
|
|
51
|
-
},
|
|
52
|
-
{
|
|
53
|
-
provide: LocalizationDataSourceProvider,
|
|
54
|
-
useValue: mockDataSourceProvider
|
|
55
|
-
}
|
|
56
|
-
]
|
|
57
|
-
}).compile();
|
|
58
|
-
service = await module.resolve(LanguageService);
|
|
59
|
-
});
|
|
60
|
-
describe('getDefaultLanguage', ()=>{
|
|
61
|
-
it('returns the language flagged as default', async ()=>{
|
|
62
|
-
const defaultLanguage = buildLanguage({
|
|
63
|
-
id: 'default-lang',
|
|
64
|
-
isDefault: true,
|
|
65
|
-
code: 'en'
|
|
66
|
-
});
|
|
67
|
-
mockRepo.findOne.mockResolvedValue(defaultLanguage);
|
|
68
|
-
const result = await service.getDefaultLanguage();
|
|
69
|
-
expect(mockRepo.findOne).toHaveBeenCalledWith({
|
|
70
|
-
where: {
|
|
71
|
-
isDefault: true,
|
|
72
|
-
deletedAt: expect.anything()
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
expect(result).toEqual(defaultLanguage);
|
|
76
|
-
});
|
|
77
|
-
it('returns null when no language is marked default', async ()=>{
|
|
78
|
-
mockRepo.findOne.mockResolvedValue(null);
|
|
79
|
-
const result = await service.getDefaultLanguage();
|
|
80
|
-
expect(result).toBeNull();
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
describe('getActiveLanguages', ()=>{
|
|
84
|
-
it('returns active, non-deleted languages ordered by serial then name', async ()=>{
|
|
85
|
-
const languages = [
|
|
86
|
-
buildLanguage({
|
|
87
|
-
code: 'en',
|
|
88
|
-
serial: 1
|
|
89
|
-
}),
|
|
90
|
-
buildLanguage({
|
|
91
|
-
code: 'bn',
|
|
92
|
-
serial: 2
|
|
93
|
-
})
|
|
94
|
-
];
|
|
95
|
-
mockRepo.find.mockResolvedValue(languages);
|
|
96
|
-
const result = await service.getActiveLanguages();
|
|
97
|
-
expect(mockRepo.find).toHaveBeenCalledWith({
|
|
98
|
-
where: {
|
|
99
|
-
isActive: true,
|
|
100
|
-
deletedAt: expect.anything()
|
|
101
|
-
},
|
|
102
|
-
order: {
|
|
103
|
-
serial: 'ASC',
|
|
104
|
-
name: 'ASC'
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
expect(result).toHaveLength(2);
|
|
108
|
-
});
|
|
109
|
-
it('returns an empty array when no languages are active', async ()=>{
|
|
110
|
-
mockRepo.find.mockResolvedValue([]);
|
|
111
|
-
const result = await service.getActiveLanguages();
|
|
112
|
-
expect(result).toEqual([]);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
describe('setAsDefault', ()=>{
|
|
116
|
-
beforeEach(()=>{
|
|
117
|
-
mockRepo.find.mockResolvedValue([]);
|
|
118
|
-
});
|
|
119
|
-
it('unsets the previous default and sets the requested language as default', async ()=>{
|
|
120
|
-
const newDefault = buildLanguage({
|
|
121
|
-
id: 'lang-2',
|
|
122
|
-
isDefault: true,
|
|
123
|
-
code: 'bn'
|
|
124
|
-
});
|
|
125
|
-
mockRepo.findOne.mockResolvedValue(newDefault);
|
|
126
|
-
const result = await service.setAsDefault('lang-2');
|
|
127
|
-
expect(mockRepo.update).toHaveBeenNthCalledWith(1, {
|
|
128
|
-
isDefault: true
|
|
129
|
-
}, {
|
|
130
|
-
isDefault: false
|
|
131
|
-
});
|
|
132
|
-
expect(mockRepo.update).toHaveBeenNthCalledWith(2, {
|
|
133
|
-
id: 'lang-2'
|
|
134
|
-
}, {
|
|
135
|
-
isDefault: true
|
|
136
|
-
});
|
|
137
|
-
expect(mockRepo.findOne).toHaveBeenCalledWith({
|
|
138
|
-
where: {
|
|
139
|
-
id: 'lang-2'
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
expect(result).toEqual(newDefault);
|
|
143
|
-
});
|
|
144
|
-
it('invalidates the language list cache and both the old and new default entries', async ()=>{
|
|
145
|
-
const previousDefault = buildLanguage({
|
|
146
|
-
id: 'lang-1',
|
|
147
|
-
isDefault: true,
|
|
148
|
-
code: 'en'
|
|
149
|
-
});
|
|
150
|
-
mockRepo.find.mockResolvedValue([
|
|
151
|
-
previousDefault
|
|
152
|
-
]);
|
|
153
|
-
mockRepo.findOne.mockResolvedValue(buildLanguage({
|
|
154
|
-
id: 'lang-2',
|
|
155
|
-
isDefault: true
|
|
156
|
-
}));
|
|
157
|
-
await service.setAsDefault('lang-2');
|
|
158
|
-
expect(mockUtilsService.clearCache).toHaveBeenCalledWith('language', expect.anything());
|
|
159
|
-
expect(mockUtilsService.clearCache).toHaveBeenCalledWith('language', expect.anything(), 'lang-1');
|
|
160
|
-
expect(mockUtilsService.clearCache).toHaveBeenCalledWith('language', expect.anything(), 'lang-2');
|
|
161
|
-
});
|
|
162
|
-
it('returns null when the id does not correspond to any language after the update', async ()=>{
|
|
163
|
-
mockRepo.findOne.mockResolvedValue(null);
|
|
164
|
-
const result = await service.setAsDefault('non-existent-id');
|
|
165
|
-
expect(result).toBeNull();
|
|
166
|
-
});
|
|
167
|
-
it('still unsets other defaults first even when the target id is unknown (no exception thrown)', async ()=>{
|
|
168
|
-
mockRepo.findOne.mockResolvedValue(null);
|
|
169
|
-
await service.setAsDefault('unknown-id');
|
|
170
|
-
expect(mockRepo.update).toHaveBeenCalledWith({
|
|
171
|
-
isDefault: true
|
|
172
|
-
}, {
|
|
173
|
-
isDefault: false
|
|
174
|
-
});
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
describe('beforeUpdateOperation', ()=>{
|
|
178
|
-
it('rejects renaming the code of a readonly language', async ()=>{
|
|
179
|
-
mockRepo.find.mockResolvedValue([
|
|
180
|
-
buildLanguage({
|
|
181
|
-
isReadonly: true,
|
|
182
|
-
code: 'en'
|
|
183
|
-
})
|
|
184
|
-
]);
|
|
185
|
-
await expect(service.beforeUpdateOperation({
|
|
186
|
-
id: 'language-uuid-1',
|
|
187
|
-
code: 'fr'
|
|
188
|
-
}, null, {})).rejects.toThrow('Cannot modify code/name of readonly language');
|
|
189
|
-
});
|
|
190
|
-
it('allows updating isActive on a readonly language when identity fields are unchanged', async ()=>{
|
|
191
|
-
mockRepo.find.mockResolvedValue([
|
|
192
|
-
buildLanguage({
|
|
193
|
-
isReadonly: true,
|
|
194
|
-
code: 'en',
|
|
195
|
-
name: 'English'
|
|
196
|
-
})
|
|
197
|
-
]);
|
|
198
|
-
await expect(service.beforeUpdateOperation({
|
|
199
|
-
id: 'language-uuid-1',
|
|
200
|
-
code: 'en',
|
|
201
|
-
name: 'English',
|
|
202
|
-
isActive: false
|
|
203
|
-
}, null, {})).resolves.toBeUndefined();
|
|
204
|
-
});
|
|
205
|
-
it('allows any field change on a non-readonly language', async ()=>{
|
|
206
|
-
mockRepo.find.mockResolvedValue([
|
|
207
|
-
buildLanguage({
|
|
208
|
-
isReadonly: false,
|
|
209
|
-
code: 'en'
|
|
210
|
-
})
|
|
211
|
-
]);
|
|
212
|
-
await expect(service.beforeUpdateOperation({
|
|
213
|
-
id: 'language-uuid-1',
|
|
214
|
-
code: 'fr'
|
|
215
|
-
}, null, {})).resolves.toBeUndefined();
|
|
216
|
-
});
|
|
217
|
-
});
|
|
218
|
-
describe('beforeDeleteOperation', ()=>{
|
|
219
|
-
it('rejects deleting a readonly language', async ()=>{
|
|
220
|
-
mockRepo.find.mockResolvedValue([
|
|
221
|
-
buildLanguage({
|
|
222
|
-
isReadonly: true
|
|
223
|
-
})
|
|
224
|
-
]);
|
|
225
|
-
await expect(service.beforeDeleteOperation({
|
|
226
|
-
id: 'language-uuid-1'
|
|
227
|
-
}, null, {})).rejects.toThrow('Cannot delete readonly language');
|
|
228
|
-
});
|
|
229
|
-
it('rejects deleting the default language', async ()=>{
|
|
230
|
-
mockRepo.find.mockResolvedValue([
|
|
231
|
-
buildLanguage({
|
|
232
|
-
isReadonly: false,
|
|
233
|
-
isDefault: true
|
|
234
|
-
})
|
|
235
|
-
]);
|
|
236
|
-
await expect(service.beforeDeleteOperation({
|
|
237
|
-
id: 'language-uuid-1'
|
|
238
|
-
}, null, {})).rejects.toThrow('Cannot delete the default language');
|
|
239
|
-
});
|
|
240
|
-
it('allows deleting a non-readonly, non-default language', async ()=>{
|
|
241
|
-
mockRepo.find.mockResolvedValue([
|
|
242
|
-
buildLanguage({
|
|
243
|
-
isReadonly: false,
|
|
244
|
-
isDefault: false
|
|
245
|
-
})
|
|
246
|
-
]);
|
|
247
|
-
await expect(service.beforeDeleteOperation({
|
|
248
|
-
id: 'language-uuid-1'
|
|
249
|
-
}, null, {})).resolves.toBeUndefined();
|
|
250
|
-
});
|
|
251
|
-
});
|
|
252
|
-
});
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
-
}
|
|
20
|
-
function _ts_metadata(k, v) {
|
|
21
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
-
}
|
|
23
|
-
function _ts_param(paramIndex, decorator) {
|
|
24
|
-
return function(target, key) {
|
|
25
|
-
decorator(target, key, paramIndex);
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
import { Inject, Injectable } from '@nestjs/common';
|
|
29
|
-
import { LOCALIZATION_MODULE_OPTIONS } from '../config/localization.constants';
|
|
30
|
-
import { LocalizationModuleOptions } from '../interfaces';
|
|
31
|
-
export class LocalizationConfigService {
|
|
32
|
-
isCompanyFeatureEnabled() {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
getDatabaseMode() {
|
|
36
|
-
return this.options.bootstrapAppConfig?.databaseMode ?? 'single';
|
|
37
|
-
}
|
|
38
|
-
isMultiTenant() {
|
|
39
|
-
return this.getDatabaseMode() === 'multi-tenant';
|
|
40
|
-
}
|
|
41
|
-
getOptions() {
|
|
42
|
-
return this.options;
|
|
43
|
-
}
|
|
44
|
-
constructor(options){
|
|
45
|
-
_define_property(this, "options", void 0);
|
|
46
|
-
this.options = options;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
LocalizationConfigService = _ts_decorate([
|
|
50
|
-
Injectable(),
|
|
51
|
-
_ts_param(0, Inject(LOCALIZATION_MODULE_OPTIONS)),
|
|
52
|
-
_ts_metadata("design:type", Function),
|
|
53
|
-
_ts_metadata("design:paramtypes", [
|
|
54
|
-
typeof LocalizationModuleOptions === "undefined" ? Object : LocalizationModuleOptions
|
|
55
|
-
])
|
|
56
|
-
], LocalizationConfigService);
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { LocalizationConfigService } from './localization-config.service';
|
|
2
|
-
function buildOptions(overrides = {}) {
|
|
3
|
-
return {
|
|
4
|
-
bootstrapAppConfig: undefined,
|
|
5
|
-
config: undefined,
|
|
6
|
-
...overrides
|
|
7
|
-
};
|
|
8
|
-
}
|
|
9
|
-
describe('LocalizationConfigService', ()=>{
|
|
10
|
-
describe('isCompanyFeatureEnabled', ()=>{
|
|
11
|
-
it('always returns false — localization module has no company scoping', ()=>{
|
|
12
|
-
const service = new LocalizationConfigService(buildOptions());
|
|
13
|
-
expect(service.isCompanyFeatureEnabled()).toBe(false);
|
|
14
|
-
});
|
|
15
|
-
it('returns false even when bootstrapAppConfig enables the company feature elsewhere', ()=>{
|
|
16
|
-
const service = new LocalizationConfigService(buildOptions({
|
|
17
|
-
bootstrapAppConfig: {
|
|
18
|
-
databaseMode: 'single',
|
|
19
|
-
enableCompanyFeature: true
|
|
20
|
-
}
|
|
21
|
-
}));
|
|
22
|
-
expect(service.isCompanyFeatureEnabled()).toBe(false);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
describe('getDatabaseMode', ()=>{
|
|
26
|
-
it('returns the configured database mode', ()=>{
|
|
27
|
-
const service = new LocalizationConfigService(buildOptions({
|
|
28
|
-
bootstrapAppConfig: {
|
|
29
|
-
databaseMode: 'multi-tenant'
|
|
30
|
-
}
|
|
31
|
-
}));
|
|
32
|
-
expect(service.getDatabaseMode()).toBe('multi-tenant');
|
|
33
|
-
});
|
|
34
|
-
it('defaults to "single" when bootstrapAppConfig is missing', ()=>{
|
|
35
|
-
const service = new LocalizationConfigService(buildOptions({
|
|
36
|
-
bootstrapAppConfig: undefined
|
|
37
|
-
}));
|
|
38
|
-
expect(service.getDatabaseMode()).toBe('single');
|
|
39
|
-
});
|
|
40
|
-
it('defaults to "single" when databaseMode is not set on bootstrapAppConfig', ()=>{
|
|
41
|
-
const service = new LocalizationConfigService(buildOptions({
|
|
42
|
-
bootstrapAppConfig: {}
|
|
43
|
-
}));
|
|
44
|
-
expect(service.getDatabaseMode()).toBe('single');
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
describe('isMultiTenant', ()=>{
|
|
48
|
-
it('returns true when database mode is multi-tenant', ()=>{
|
|
49
|
-
const service = new LocalizationConfigService(buildOptions({
|
|
50
|
-
bootstrapAppConfig: {
|
|
51
|
-
databaseMode: 'multi-tenant'
|
|
52
|
-
}
|
|
53
|
-
}));
|
|
54
|
-
expect(service.isMultiTenant()).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
it('returns false when database mode is single', ()=>{
|
|
57
|
-
const service = new LocalizationConfigService(buildOptions({
|
|
58
|
-
bootstrapAppConfig: {
|
|
59
|
-
databaseMode: 'single'
|
|
60
|
-
}
|
|
61
|
-
}));
|
|
62
|
-
expect(service.isMultiTenant()).toBe(false);
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
describe('getOptions', ()=>{
|
|
66
|
-
it('returns the exact options object it was constructed with', ()=>{
|
|
67
|
-
const options = buildOptions({
|
|
68
|
-
config: {
|
|
69
|
-
defaultLanguageCode: 'en'
|
|
70
|
-
}
|
|
71
|
-
});
|
|
72
|
-
const service = new LocalizationConfigService(options);
|
|
73
|
-
expect(service.getOptions()).toBe(options);
|
|
74
|
-
});
|
|
75
|
-
});
|
|
76
|
-
});
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
function _define_property(obj, key, value) {
|
|
2
|
-
if (key in obj) {
|
|
3
|
-
Object.defineProperty(obj, key, {
|
|
4
|
-
value: value,
|
|
5
|
-
enumerable: true,
|
|
6
|
-
configurable: true,
|
|
7
|
-
writable: true
|
|
8
|
-
});
|
|
9
|
-
} else {
|
|
10
|
-
obj[key] = value;
|
|
11
|
-
}
|
|
12
|
-
return obj;
|
|
13
|
-
}
|
|
14
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
15
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
16
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
17
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
18
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
19
|
-
}
|
|
20
|
-
function _ts_metadata(k, v) {
|
|
21
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
22
|
-
}
|
|
23
|
-
function _ts_param(paramIndex, decorator) {
|
|
24
|
-
return function(target, key) {
|
|
25
|
-
decorator(target, key, paramIndex);
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
import { MultiTenantDataSourceService } from '@flusys/nestjs-shared/modules';
|
|
29
|
-
import { Inject, Injectable, Optional, Scope } from '@nestjs/common';
|
|
30
|
-
import { REQUEST } from '@nestjs/core';
|
|
31
|
-
import { Request } from 'express';
|
|
32
|
-
import { getLocalizationEntities } from '../entities';
|
|
33
|
-
import { LocalizationConfigService } from './localization-config.service';
|
|
34
|
-
export class LocalizationDataSourceProvider extends MultiTenantDataSourceService {
|
|
35
|
-
static buildParentOptions(options) {
|
|
36
|
-
return {
|
|
37
|
-
bootstrapAppConfig: options.bootstrapAppConfig,
|
|
38
|
-
defaultDatabaseConfig: options.config?.defaultDatabaseConfig,
|
|
39
|
-
tenantDefaultDatabaseConfig: options.config?.tenantDefaultDatabaseConfig,
|
|
40
|
-
tenants: options.config?.tenants
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
async createDataSourceFromConfig(config) {
|
|
44
|
-
const entities = getLocalizationEntities();
|
|
45
|
-
return super.createDataSourceFromConfig(config, entities);
|
|
46
|
-
}
|
|
47
|
-
constructor(configService, request){
|
|
48
|
-
super(LocalizationDataSourceProvider.buildParentOptions(configService.getOptions()), request);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
_define_property(LocalizationDataSourceProvider, "tenantConnections", new Map());
|
|
52
|
-
_define_property(LocalizationDataSourceProvider, "singleDataSource", null);
|
|
53
|
-
_define_property(LocalizationDataSourceProvider, "tenantsRegistry", new Map());
|
|
54
|
-
_define_property(LocalizationDataSourceProvider, "initialized", false);
|
|
55
|
-
_define_property(LocalizationDataSourceProvider, "connectionLocks", new Map());
|
|
56
|
-
_define_property(LocalizationDataSourceProvider, "singleConnectionLock", null);
|
|
57
|
-
LocalizationDataSourceProvider = _ts_decorate([
|
|
58
|
-
Injectable({
|
|
59
|
-
scope: Scope.REQUEST
|
|
60
|
-
}),
|
|
61
|
-
_ts_param(0, Inject(LocalizationConfigService)),
|
|
62
|
-
_ts_param(1, Optional()),
|
|
63
|
-
_ts_param(1, Inject(REQUEST)),
|
|
64
|
-
_ts_metadata("design:type", Function),
|
|
65
|
-
_ts_metadata("design:paramtypes", [
|
|
66
|
-
typeof LocalizationConfigService === "undefined" ? Object : LocalizationConfigService,
|
|
67
|
-
typeof Request === "undefined" ? Object : Request
|
|
68
|
-
])
|
|
69
|
-
], LocalizationDataSourceProvider);
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { LocalizationDataSourceProvider } from './localization-datasource.provider';
|
|
2
|
-
import { Language, Translation, TranslationKey } from '../entities';
|
|
3
|
-
function buildConfigService(overrides = {}) {
|
|
4
|
-
const options = {
|
|
5
|
-
bootstrapAppConfig: {
|
|
6
|
-
databaseMode: 'single'
|
|
7
|
-
},
|
|
8
|
-
config: {
|
|
9
|
-
defaultDatabaseConfig: {
|
|
10
|
-
type: 'mysql',
|
|
11
|
-
host: 'localhost',
|
|
12
|
-
port: 3306,
|
|
13
|
-
username: 'root',
|
|
14
|
-
password: 'password',
|
|
15
|
-
database: 'flusys_test'
|
|
16
|
-
},
|
|
17
|
-
tenantDefaultDatabaseConfig: undefined,
|
|
18
|
-
tenants: []
|
|
19
|
-
},
|
|
20
|
-
...overrides
|
|
21
|
-
};
|
|
22
|
-
return {
|
|
23
|
-
getOptions: jest.fn().mockReturnValue(options)
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
describe('LocalizationDataSourceProvider', ()=>{
|
|
27
|
-
afterEach(()=>{
|
|
28
|
-
// Reset the isolated static caches this provider owns so tests don't leak state between runs.
|
|
29
|
-
LocalizationDataSourceProvider.tenantConnections = new Map();
|
|
30
|
-
LocalizationDataSourceProvider.singleDataSource = null;
|
|
31
|
-
LocalizationDataSourceProvider.tenantsRegistry = new Map();
|
|
32
|
-
LocalizationDataSourceProvider.initialized = false;
|
|
33
|
-
LocalizationDataSourceProvider.connectionLocks = new Map();
|
|
34
|
-
LocalizationDataSourceProvider.singleConnectionLock = null;
|
|
35
|
-
});
|
|
36
|
-
it('constructs and forwards bootstrap/database config to the base class', ()=>{
|
|
37
|
-
const configService = buildConfigService();
|
|
38
|
-
const provider = new LocalizationDataSourceProvider(configService, undefined);
|
|
39
|
-
expect(configService.getOptions).toHaveBeenCalled();
|
|
40
|
-
expect(provider.getDatabaseMode()).toBe('single');
|
|
41
|
-
expect(provider.isMultiTenant()).toBe(false);
|
|
42
|
-
});
|
|
43
|
-
it('reflects multi-tenant mode from bootstrapAppConfig', ()=>{
|
|
44
|
-
const configService = buildConfigService({
|
|
45
|
-
bootstrapAppConfig: {
|
|
46
|
-
databaseMode: 'multi-tenant'
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
const provider = new LocalizationDataSourceProvider(configService, undefined);
|
|
50
|
-
expect(provider.isMultiTenant()).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
it('owns isolated static caches, separate from the base MultiTenantDataSourceService', ()=>{
|
|
53
|
-
// Each DataSource Provider must override BOTH static cache properties and the
|
|
54
|
-
// methods that read them — otherwise modules would share connections (see FLUSYS_NEST/CLAUDE.md).
|
|
55
|
-
expect(LocalizationDataSourceProvider.tenantConnections).not.toBe(Object.getPrototypeOf(LocalizationDataSourceProvider).tenantConnections);
|
|
56
|
-
});
|
|
57
|
-
it('createDataSourceFromConfig builds a DataSource using all localization entities', async ()=>{
|
|
58
|
-
const configService = buildConfigService();
|
|
59
|
-
const provider = new LocalizationDataSourceProvider(configService, undefined);
|
|
60
|
-
const fakeDataSource = {
|
|
61
|
-
isInitialized: true
|
|
62
|
-
};
|
|
63
|
-
const superSpy = jest.spyOn(Object.getPrototypeOf(Object.getPrototypeOf(provider)), 'createDataSourceFromConfig').mockResolvedValue(fakeDataSource);
|
|
64
|
-
const config = {
|
|
65
|
-
type: 'mysql',
|
|
66
|
-
host: 'localhost',
|
|
67
|
-
port: 3306,
|
|
68
|
-
username: 'root',
|
|
69
|
-
password: 'password',
|
|
70
|
-
database: 'flusys_test'
|
|
71
|
-
};
|
|
72
|
-
const result = await provider.createDataSourceFromConfig(config);
|
|
73
|
-
expect(superSpy).toHaveBeenCalledWith(config, [
|
|
74
|
-
Language,
|
|
75
|
-
TranslationKey,
|
|
76
|
-
Translation
|
|
77
|
-
]);
|
|
78
|
-
expect(result).toBe(fakeDataSource);
|
|
79
|
-
superSpy.mockRestore();
|
|
80
|
-
});
|
|
81
|
-
});
|