xcally-nest-library 0.0.35 → 0.0.37-XM3778-bae91

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.
Files changed (85) hide show
  1. package/.jest/jestEnv.js +3 -0
  2. package/.nvmrc +1 -1
  3. package/.prettierrc +18 -1
  4. package/dist/index.d.ts +9 -0
  5. package/dist/index.js +9 -0
  6. package/dist/index.js.map +1 -1
  7. package/dist/jest.config.d.ts +3 -0
  8. package/dist/jest.config.js +61 -0
  9. package/dist/jest.config.js.map +1 -0
  10. package/dist/src/core/application/__tests__/cached-base.service.spec.d.ts +1 -0
  11. package/dist/src/core/application/__tests__/cached-base.service.spec.js +224 -0
  12. package/dist/src/core/application/__tests__/cached-base.service.spec.js.map +1 -0
  13. package/dist/src/core/application/__tests__/mock-types.d.ts +66 -0
  14. package/dist/src/core/application/__tests__/mock-types.js +9 -0
  15. package/dist/src/core/application/__tests__/mock-types.js.map +1 -0
  16. package/dist/src/core/application/__tests__/mocks/paginated-query.mock.d.ts +2 -0
  17. package/dist/src/core/application/__tests__/mocks/paginated-query.mock.js +12 -0
  18. package/dist/src/core/application/__tests__/mocks/paginated-query.mock.js.map +1 -0
  19. package/dist/src/core/application/__tests__/mocks/paginated-result.mock.d.ts +31 -0
  20. package/dist/src/core/application/__tests__/mocks/paginated-result.mock.js +169 -0
  21. package/dist/src/core/application/__tests__/mocks/paginated-result.mock.js.map +1 -0
  22. package/dist/src/core/application/base.interface.d.ts +4 -4
  23. package/dist/src/core/application/base.mongo.interface.d.ts +16 -0
  24. package/dist/src/core/application/base.mongo.interface.js +3 -0
  25. package/dist/src/core/application/base.mongo.interface.js.map +1 -0
  26. package/dist/src/core/application/base.mongo.service.d.ts +23 -0
  27. package/dist/src/core/application/base.mongo.service.js +91 -0
  28. package/dist/src/core/application/base.mongo.service.js.map +1 -0
  29. package/dist/src/core/application/base.service.d.ts +2 -2
  30. package/dist/src/core/application/cached-base.service.d.ts +26 -0
  31. package/dist/src/core/application/cached-base.service.js +52 -0
  32. package/dist/src/core/application/cached-base.service.js.map +1 -0
  33. package/dist/src/core/application/cached.service.d.ts +13 -0
  34. package/dist/src/core/application/cached.service.js +55 -0
  35. package/dist/src/core/application/cached.service.js.map +1 -0
  36. package/dist/src/core/domain/interfaces/base.mongo.repository.interface.d.ts +12 -0
  37. package/dist/src/core/domain/interfaces/base.mongo.repository.interface.js +3 -0
  38. package/dist/src/core/domain/interfaces/base.mongo.repository.interface.js.map +1 -0
  39. package/dist/src/core/domain/interfaces/cache-configuration.interface.d.ts +8 -0
  40. package/dist/src/core/domain/interfaces/cache-configuration.interface.js +11 -0
  41. package/dist/src/core/domain/interfaces/cache-configuration.interface.js.map +1 -0
  42. package/dist/src/core/infrastracture/databases/base.mongo.entity.d.ts +14 -0
  43. package/dist/src/core/infrastracture/databases/base.mongo.entity.interface.d.ts +10 -0
  44. package/dist/src/core/infrastracture/databases/base.mongo.entity.interface.js +3 -0
  45. package/dist/src/core/infrastracture/databases/base.mongo.entity.interface.js.map +1 -0
  46. package/dist/src/core/infrastracture/databases/base.mongo.entity.js +66 -0
  47. package/dist/src/core/infrastracture/databases/base.mongo.entity.js.map +1 -0
  48. package/dist/src/core/infrastracture/databases/base.mongo.repository.d.ts +20 -0
  49. package/dist/src/core/infrastracture/databases/base.mongo.repository.js +275 -0
  50. package/dist/src/core/infrastracture/databases/base.mongo.repository.js.map +1 -0
  51. package/dist/src/core/infrastracture/databases/base.repository.d.ts +1 -1
  52. package/dist/src/modules/logger/pino/logger.module.js +1 -1
  53. package/dist/src/modules/logger/pino/logger.module.js.map +1 -1
  54. package/dist/src/modules/logger/pino/logger.service.js +1 -1
  55. package/dist/src/modules/logger/pino/logger.service.js.map +1 -1
  56. package/dist/tsconfig.json +38 -0
  57. package/dist/tsconfig.tsbuildinfo +1 -1
  58. package/eslint.config.mjs +49 -0
  59. package/index.ts +9 -0
  60. package/jest.config.ts +219 -0
  61. package/package.json +34 -21
  62. package/scripts/publish-feat.sh +67 -0
  63. package/src/core/application/__tests__/cached-base.service.spec.ts +367 -0
  64. package/src/core/application/__tests__/mock-types.ts +73 -0
  65. package/src/core/application/__tests__/mocks/paginated-query.mock.ts +10 -0
  66. package/src/core/application/__tests__/mocks/paginated-result.mock.ts +166 -0
  67. package/src/core/application/base.interface.ts +4 -5
  68. package/src/core/application/base.mongo.interface.ts +15 -0
  69. package/src/core/application/base.mongo.service.ts +170 -0
  70. package/src/core/application/base.service.ts +2 -2
  71. package/src/core/application/cached-base.service.ts +119 -0
  72. package/src/core/application/cached.service.ts +96 -0
  73. package/src/core/domain/interfaces/base.mongo.repository.interface.ts +14 -0
  74. package/src/core/domain/interfaces/base.repository.interface.ts +1 -0
  75. package/src/core/domain/interfaces/cache-configuration.interface.ts +10 -0
  76. package/src/core/infrastracture/databases/base.mongo.entity.interface.ts +12 -0
  77. package/src/core/infrastracture/databases/base.mongo.entity.ts +65 -0
  78. package/src/core/infrastracture/databases/base.mongo.repository.ts +371 -0
  79. package/src/core/infrastracture/databases/base.repository.ts +1 -1
  80. package/src/modules/logger/pino/logger.module.ts +1 -1
  81. package/src/modules/logger/pino/logger.service.ts +1 -1
  82. package/tsconfig.json +1 -0
  83. package/dist/src/modules/db-hooks-subscriber/db-hooks-subscriber.module.d.ts +0 -2
  84. package/dist/src/modules/db-hooks-subscriber/db-hooks-subscriber.module.js +0 -22
  85. package/dist/src/modules/db-hooks-subscriber/db-hooks-subscriber.module.js.map +0 -1
@@ -0,0 +1,170 @@
1
+ import { GrpcNotFoundException } from 'nestjs-grpc-exceptions';
2
+ import { Paginated, PaginateQuery } from 'nestjs-paginate';
3
+ import { IBaseMongoRepository } from '@/core/domain/interfaces/base.mongo.repository.interface';
4
+ import { instanceToPlain } from 'class-transformer';
5
+ import { ObjectId } from 'mongodb';
6
+ import { BaseMongoEntity } from '../infrastracture/databases/base.mongo.entity';
7
+ import { IBaseService } from './base.interface';
8
+
9
+ export class BaseMongoService<
10
+ Entity,
11
+ CreateMessage,
12
+ UpdateMessage extends { id: string },
13
+ Message,
14
+ > implements IBaseService<CreateMessage, UpdateMessage, Message>
15
+ {
16
+ constructor(private readonly repository: IBaseMongoRepository<Entity>) {}
17
+
18
+ /**
19
+ * Finds a single entity by its ID.
20
+ * @param id - The ID of the entity to find (string or ObjectId).
21
+ * @returns A promise that resolves to the mapped message.
22
+ * @throws GrpcNotFoundException if the entity is not found.
23
+ */
24
+ async findOne(id: string): Promise<Message> {
25
+ const entity = (await this.repository.findById(id)) as BaseMongoEntity;
26
+ const message = this.transformMongoEntityToMessage(entity);
27
+ if (!entity) {
28
+ throw new GrpcNotFoundException(`${this.constructor.name.replace('Service', '')} not found`);
29
+ }
30
+ return message;
31
+ }
32
+
33
+ /**
34
+ * Retrieves all entities.
35
+ * @returns A promise that resolves to an object containing an array of mapped messages.
36
+ */
37
+ async findAll(): Promise<{ data: Message[] }> {
38
+ const entities: Entity[] = await this.repository.find();
39
+ return {
40
+ data: entities as unknown as Message[],
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Retrieves paginated entities based on the provided query.
46
+ * @param query - Pagination and filter criteria.
47
+ * @returns A promise that resolves to the paginated result with rows and count.
48
+ */
49
+ async getMany(query?: PaginateQuery): Promise<Paginated<Message>> {
50
+ if (!query)
51
+ return this.transformPaginatedMongoEntityToPaginatedMessage(
52
+ (await this.repository.getMany({ path: '' })) as unknown as Paginated<BaseMongoEntity>,
53
+ );
54
+
55
+
56
+ const modifiedQuery: PaginateQuery = { ...query };
57
+
58
+ //
59
+ // select → switch "id" to "_id"
60
+ //
61
+ if (Array.isArray(modifiedQuery.select)) {
62
+ modifiedQuery.select = modifiedQuery.select.map(f => (f === 'id' ? '_id' : f));
63
+ }
64
+
65
+ //
66
+ // sortBy → array of arrays [[field, direction]] and switch "id" to "_id"
67
+ //
68
+ if (Array.isArray(modifiedQuery.sortBy)) {
69
+ modifiedQuery.sortBy = modifiedQuery.sortBy.map(([field, dir]) => [field === 'id' ? '_id' : field, dir]);
70
+ }
71
+
72
+ //
73
+ // filter → complex object { fieldName: value } and switch "id" to "_id"
74
+ //
75
+ if (modifiedQuery.filter && typeof modifiedQuery.filter === 'object') {
76
+ const newFilter: Record<string, any> = {};
77
+ for (const [key, val] of Object.entries(modifiedQuery.filter)) {
78
+ const newKey = key.startsWith('id') ? key.replace(/^id\b/, '_id') : key === 'id' ? '_id' : key;
79
+ newFilter[newKey] = val;
80
+ }
81
+ modifiedQuery.filter = newFilter;
82
+ }
83
+
84
+ //
85
+ // cursorColumn switch "id" to "_id"
86
+ //
87
+ if (modifiedQuery.cursorColumn === 'id') {
88
+ modifiedQuery.cursorColumn = '_id';
89
+ }
90
+
91
+ const entities = (await this.repository.getMany(modifiedQuery)) as unknown as Paginated<BaseMongoEntity>;
92
+
93
+ // switch back _id to id for the response
94
+ return this.transformPaginatedMongoEntityToPaginatedMessage(entities);
95
+ }
96
+
97
+ // simple switch id to _id
98
+ protected transformMongoEntityToMessage(entity: BaseMongoEntity): Message {
99
+ return { ...entity, id: entity._id.toString() } as unknown as Message;
100
+ }
101
+
102
+ // switch id to _id in the pagination object
103
+ public transformPaginatedMongoEntityToPaginatedMessage(
104
+ paginatedEntities: Paginated<BaseMongoEntity>,
105
+ ): Paginated<Message> {
106
+ const transformedData = paginatedEntities.data.map(entity => {
107
+ return this.transformMongoEntityToMessage(entity);
108
+ });
109
+
110
+ const paginatedMessages: Paginated<Message> = {
111
+ links: { ...paginatedEntities.links },
112
+ meta: { ...paginatedEntities.meta } as unknown as Paginated<Message>['meta'],
113
+ data: transformedData,
114
+ };
115
+
116
+ return paginatedMessages;
117
+ }
118
+
119
+ /**
120
+ * Creates a new entity from the provided DTO.
121
+ * @param createDTO - The data transfer object for creating an entity.
122
+ * @returns A promise that resolves to the mapped message of the created entity.
123
+ */
124
+ async create(createDTO: CreateMessage): Promise<Message> {
125
+ const entity = this.repository.create(createDTO as unknown as Entity);
126
+ for (const key of this.repository.paginateConfig.dateColumns) {
127
+ entity[key] = createDTO[key] ? new Date(createDTO[key]) : null;
128
+ }
129
+ const savedEntity = await this.repository.save(entity);
130
+ return this.transformMongoEntityToMessage(savedEntity as unknown as BaseMongoEntity);
131
+ }
132
+
133
+ /**
134
+ * Updates an existing entity with the provided DTO.
135
+ * @param updateDTO - The data transfer object for updating an entity.
136
+ * @returns A promise that resolves to the mapped message of the updated entity.
137
+ * @throws GrpcNotFoundException if the entity is not found.
138
+ */
139
+ async update(updateDTO: UpdateMessage): Promise<Message> {
140
+ const entity = await this.repository.findById(updateDTO.id);
141
+ if (!entity) {
142
+ throw new GrpcNotFoundException(`${this.constructor.name.replace('Service', '')} not found`);
143
+ }
144
+
145
+ const objectId = new ObjectId(updateDTO.id);
146
+ await this.repository.update(objectId as any, instanceToPlain(updateDTO) as any);
147
+ const updatedEntity: Entity = await this.repository.findById(updateDTO.id);
148
+ return this.transformMongoEntityToMessage(updatedEntity as unknown as BaseMongoEntity);
149
+ }
150
+
151
+ /**
152
+ * Deletes an entity by its ID.
153
+ * @param id - The ID of the entity to delete (string).
154
+ * @returns A promise that resolves when the entity is deleted.
155
+ */
156
+ async remove(id: string): Promise<void> {
157
+ const objectId = new ObjectId(id);
158
+ await this.repository.delete(objectId as any);
159
+ }
160
+
161
+ /**
162
+ * Counts all entities.
163
+ * @param options - Optional query options for counting entities.
164
+ * @returns A promise that resolves to an object containing the count.
165
+ */
166
+ async count(options?: any): Promise<{ count: number }> {
167
+ const count = await this.repository.count(options || {});
168
+ return { count };
169
+ }
170
+ }
@@ -1,6 +1,6 @@
1
1
  import { GrpcNotFoundException } from 'nestjs-grpc-exceptions';
2
2
  import { Paginated, PaginateQuery } from 'nestjs-paginate';
3
- import { IBaseRepository } from '@domain/interfaces/base.repository.interface';
3
+ import { IBaseRepository } from '@/core/domain/interfaces/base.repository.interface';
4
4
  import { instanceToPlain } from 'class-transformer';
5
5
  import { IBaseService } from './base.interface';
6
6
 
@@ -9,7 +9,7 @@ export class BaseService<
9
9
  CreateMessage extends Partial<Entity>,
10
10
  UpdateMessage extends Partial<Entity> & { id: number },
11
11
  Message,
12
- > implements IBaseService<Entity, CreateMessage, UpdateMessage, Message>
12
+ > implements IBaseService<CreateMessage, UpdateMessage, Message>
13
13
  {
14
14
  constructor(private readonly repository: IBaseRepository<Entity>) {}
15
15
 
@@ -0,0 +1,119 @@
1
+ import { LoggerService } from '@/modules/logger/pino/logger.service';
2
+ import { Cache } from '@nestjs/cache-manager';
3
+ import { Paginated, PaginateQuery } from 'nestjs-paginate';
4
+ import { CacheConfiguration, InvalidationStrategy } from '../domain/interfaces/cache-configuration.interface';
5
+ import { IBaseService } from './base.interface';
6
+ import { CachedService } from './cached.service';
7
+
8
+ /**
9
+ * Base CRUD service with caching capabilities.
10
+ */
11
+ export class CachedBaseService<
12
+ Entity,
13
+ CreateMessage,
14
+ UpdateMessage extends { id: number | string },
15
+ Message,
16
+ > extends CachedService implements IBaseService<CreateMessage, UpdateMessage, Message> {
17
+
18
+ /**
19
+ * Creates an instance of CachedBaseService.
20
+ *
21
+ * @param {LoggerService} logger - The logger service.
22
+ * @param {IBaseService<CreateMessage, UpdateMessage, Message>} service - The service to add the caching layer to.
23
+ * @param {Cache} cacheManager - The NestJS cache manager.
24
+ * @param {CacheConfiguration} cacheConfig - The cache configuration.
25
+ */
26
+ constructor(
27
+ protected readonly logger: LoggerService,
28
+ protected readonly service: IBaseService<CreateMessage, UpdateMessage, Message>,
29
+ protected readonly cacheManager: Cache,
30
+ protected readonly cacheConfig: CacheConfiguration,
31
+ ) {
32
+ super(logger, cacheManager, cacheConfig);
33
+ }
34
+
35
+ /**
36
+ * Finds a single entity by its ID with caching.
37
+ *
38
+ * @param {number | string} id - The ID of the entity to find.
39
+ * @returns {Promise<Message>} A promise that resolves to the mapped message.
40
+ */
41
+ async findOne(id: number | string): Promise<Message> {
42
+ const cacheKey = this.generateCacheKey('findOne', { id });
43
+ return await this.getOrSetCache<Message>(cacheKey, async () => await this.service.findOne(id));
44
+ }
45
+
46
+ /**
47
+ * Retrieves all entities with caching.
48
+ *
49
+ * @returns {Promise<{ data: Message[] }>} A promise that resolves to an object containing an array of mapped messages.
50
+ */
51
+ async findAll(): Promise<{ data: Message[] }> {
52
+ const cacheKey = this.generateCacheKey('findAll');
53
+ return await this.getOrSetCache(cacheKey, async () => await this.service.findAll());
54
+ }
55
+
56
+ /**
57
+ * Retrieves paginated entities based on the provided query with caching.
58
+ * The cache key includes filters to ensure correct results for different queries.
59
+ *
60
+ * @param {PaginateQuery} query - Pagination and filter criteria.
61
+ * @returns {Promise<Paginated<Message>>} A promise that resolves to the paginated result.
62
+ */
63
+ async getMany(query?: PaginateQuery): Promise<Paginated<Message>> {
64
+ const cacheKey = this.generateCacheKey('getMany', query);
65
+ return await this.getOrSetCache(cacheKey, async () => await this.service.getMany(query));
66
+ }
67
+
68
+ /**
69
+ * Creates a new entity and invalidates cache.
70
+ *
71
+ * @param {CreateMessage} createDTO - The data transfer object for creating an entity.
72
+ * @returns {Promise<Message>} A promise that resolves to the mapped message of the created entity.
73
+ */
74
+ async create(createDTO: CreateMessage): Promise<Message> {
75
+ const result = await this.service.create(createDTO);
76
+ if (this.cacheConfig.invalidationStrategy === InvalidationStrategy.OnChange) {
77
+ await this.invalidateCache();
78
+ }
79
+ return result;
80
+ }
81
+
82
+ /**
83
+ * Updates an existing entity and invalidates cache.
84
+ *
85
+ * @param {UpdateMessage} updateDTO - The data transfer object for updating an entity.
86
+ * @returns {Promise<Message>} A promise that resolves to the mapped message of the updated entity.
87
+ */
88
+ async update(updateDTO: UpdateMessage): Promise<Message> {
89
+ const result = await this.service.update(updateDTO);
90
+ if (this.cacheConfig.invalidationStrategy === InvalidationStrategy.OnChange) {
91
+ await this.invalidateCache();
92
+ }
93
+ return result;
94
+ }
95
+
96
+ /**
97
+ * Deletes an entity by its ID and invalidates cache.
98
+ *
99
+ * @param {number | string} id - The ID of the entity to delete.
100
+ * @returns {Promise<void>} A promise that resolves when the entity is deleted.
101
+ */
102
+ async remove(id: number | string): Promise<void> {
103
+ await this.service.remove(id);
104
+ if (this.cacheConfig.invalidationStrategy === InvalidationStrategy.OnChange) {
105
+ await this.invalidateCache();
106
+ }
107
+ }
108
+
109
+ /**
110
+ * Counts all entities with caching.
111
+ *
112
+ * @param {any} options - Optional query options for counting entities.
113
+ * @returns {Promise<{ count: number }>} A promise that resolves to an object containing the count.
114
+ */
115
+ async count(options?: any): Promise<{ count: number }> {
116
+ const cacheKey = this.generateCacheKey('count', options);
117
+ return await this.getOrSetCache(cacheKey, async () => await this.service.count(options));
118
+ }
119
+ }
@@ -0,0 +1,96 @@
1
+ import { LoggerService } from '@/modules/logger/pino/logger.service';
2
+ import { Cache } from '@nestjs/cache-manager';
3
+ import { createHash } from 'crypto';
4
+ import { CacheConfiguration } from '../domain/interfaces/cache-configuration.interface';
5
+
6
+ /**
7
+ * Base service with caching capabilities.
8
+ */
9
+ export class CachedService {
10
+
11
+ protected readonly serviceName = this.constructor.name.toLowerCase().replace('service', '');
12
+
13
+ /**
14
+ * Creates an instance of CachedBaseService.
15
+ *
16
+ * @param {LoggerService} logger - The logger service.
17
+ * @param {Cache} cacheManager - The NestJS cache manager.
18
+ * @param {CacheConfiguration} cacheConfig - The cache configuration.
19
+ */
20
+ constructor(
21
+ protected readonly logger: LoggerService,
22
+ protected readonly cacheManager: Cache,
23
+ protected readonly cacheConfig: CacheConfiguration,
24
+ ) { }
25
+
26
+ /**
27
+ * Generates a cache key based on the service name, method, and parameters.
28
+ *
29
+ * @param {string} method - The method name.
30
+ * @param {any} params - The parameters to include in the key.
31
+ * @returns {string} The generated cache key.
32
+ */
33
+ protected generateCacheKey(method: string, params?: any): string {
34
+ const paramsHash = params ? createHash('md5').update(JSON.stringify(params)).digest('hex') : 'all';
35
+ return `${this.serviceName}:${method}:${paramsHash}`;
36
+ }
37
+
38
+ /**
39
+ * Gets data from cache or executes the provided function and caches the result.
40
+ *
41
+ * @param {string} key - The cache key.
42
+ * @param {() => Promise<T>} fn - The function to execute if cache miss.
43
+ * @returns {Promise<T>} The cached or freshly fetched data.
44
+ */
45
+ protected async getOrSetCache<T>(key: string, fn: () => Promise<T>): Promise<T> {
46
+ try {
47
+ // Try to get from cache
48
+ const cached = await this.cacheManager.get<string>(key);
49
+
50
+ if (cached) {
51
+ this.logger.debug(`Cache hit for key: ${key}`);
52
+ return JSON.parse(cached) as T;
53
+ }
54
+
55
+ this.logger.debug(`Cache miss for key: ${key}`);
56
+
57
+ // Cache miss - fetch fresh data
58
+ const result = await fn();
59
+
60
+ // Store in cache
61
+ await this.cacheManager.set(key, JSON.stringify(result));
62
+
63
+ return result;
64
+ } catch (error) {
65
+ this.logger.error(`An error occurred while fetching cache for key: ${key}`);
66
+ this.logger.error(error);
67
+ // On failure, fallback to freshly fetched data
68
+ return await fn();
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Invalidates cache.
74
+ *
75
+ * @returns {Promise<void>}
76
+ */
77
+ protected async invalidateCache(): Promise<void> {
78
+ try {
79
+ this.logger.debug(`Invalidating cache for service: ${this.serviceName}`);
80
+ const storeIterator = this.cacheManager.stores[0].iterator;
81
+ const keys: Array<string> = [];
82
+ if (storeIterator) {
83
+ for await (const [key, value] of storeIterator('namespace')) {
84
+ if (key.split(':')[0] === this.serviceName) {
85
+ keys.push(key);
86
+ }
87
+ }
88
+ await this.cacheManager.mdel(keys);
89
+ }
90
+ } catch (error) {
91
+ // Silently fail cache invalidation to not break the main flow
92
+ this.logger.error(`An error occurred while invalidating cache for service: ${this.serviceName}`);
93
+ this.logger.error(error);
94
+ }
95
+ }
96
+ }
@@ -0,0 +1,14 @@
1
+ import { PaginateQuery, Paginated } from 'nestjs-paginate';
2
+ import { MongoRepository } from 'typeorm';
3
+ import { ObjectId } from 'mongodb';
4
+ import { ExtendedPaginateConfig } from 'index';
5
+
6
+ export interface IBaseMongoId {
7
+ _id: ObjectId;
8
+ }
9
+
10
+ export interface IBaseMongoRepository<E> extends MongoRepository<E> {
11
+ getMany(query: PaginateQuery): Promise<Paginated<E>>;
12
+ findById(id: string | ObjectId): Promise<E>;
13
+ paginateConfig: ExtendedPaginateConfig<E>;
14
+ }
@@ -1,5 +1,6 @@
1
1
  import { PaginateQuery, Paginated } from 'nestjs-paginate';
2
2
  import { Repository } from 'typeorm';
3
+
3
4
  export interface IId {
4
5
  id: number;
5
6
  }
@@ -0,0 +1,10 @@
1
+ export const CACHE_CONF = Symbol('CACHE_CONF');
2
+
3
+ export enum InvalidationStrategy {
4
+ TTL = 'TTL',
5
+ OnChange = 'OnChange',
6
+ };
7
+
8
+ export interface CacheConfiguration {
9
+ invalidationStrategy: InvalidationStrategy;
10
+ }
@@ -0,0 +1,12 @@
1
+ import { ObjectId } from 'mongodb';
2
+ import { PaginateConfig } from 'nestjs-paginate/lib/paginate';
3
+
4
+ export interface IBaseMongoEntity {
5
+ _id: ObjectId;
6
+ createdAt: Date;
7
+ updatedAt: Date;
8
+ }
9
+
10
+ export interface ExtendedPaginateConfig<E> extends PaginateConfig<E> {
11
+ dateColumns?: string[];
12
+ }
@@ -0,0 +1,65 @@
1
+ import { ObjectId } from 'mongodb';
2
+ import {
3
+ CreateDateColumn,
4
+ EntitySubscriberInterface,
5
+ EventSubscriber,
6
+ InsertEvent,
7
+ ObjectIdColumn,
8
+ UpdateDateColumn,
9
+ UpdateEvent,
10
+ } from 'typeorm';
11
+ import { ExtendedPaginateConfig, IBaseMongoEntity } from './base.mongo.entity.interface';
12
+
13
+ export class BaseMongoEntity implements IBaseMongoEntity {
14
+ @ObjectIdColumn()
15
+ _id: ObjectId;
16
+
17
+ @CreateDateColumn({
18
+ type: 'timestamp',
19
+ })
20
+ createdAt: Date;
21
+
22
+ @UpdateDateColumn({
23
+ type: 'timestamp',
24
+ })
25
+ updatedAt: Date;
26
+ }
27
+
28
+ @EventSubscriber()
29
+ export class BaseMongoEntitySubscriber implements EntitySubscriberInterface<BaseMongoEntity> {
30
+ /**
31
+ * Indicates that this subscriber only listens to BaseMongoEntity events.
32
+ */
33
+ public listenTo() {
34
+ return BaseMongoEntity;
35
+ }
36
+
37
+ /**
38
+ * Called before entity insertion.
39
+ */
40
+ public beforeInsert(event: InsertEvent<BaseMongoEntity>) {
41
+ const now = new Date();
42
+ now.setMilliseconds((now.getMilliseconds() + 1) % 1000);
43
+ event.entity.createdAt = now;
44
+ event.entity.updatedAt = now;
45
+ }
46
+
47
+ public beforeUpdate(event: UpdateEvent<BaseMongoEntity>): void | Promise<any> {
48
+ const now = new Date();
49
+ now.setMilliseconds((now.getMilliseconds() + 1) % 1000);
50
+ event.entity.updatedAt = now;
51
+ }
52
+ }
53
+
54
+ export const paginationConfig: ExtendedPaginateConfig<BaseMongoEntity> = {
55
+ sortableColumns: ['_id', 'createdAt', 'updatedAt'],
56
+ searchableColumns: ['_id', 'createdAt', 'updatedAt'],
57
+ filterableColumns: {
58
+ _id: true,
59
+ createdAt: true,
60
+ updatedAt: true,
61
+ },
62
+ select: ['_id', 'createdAt', 'updatedAt'],
63
+ relativePath: true,
64
+ dateColumns: ['createdAt', 'updatedAt'],
65
+ };