@smartsoft001/mongo 2.75.0 → 2.80.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.
@@ -1,259 +0,0 @@
1
- import { MongoClient, GridFSBucket } from 'mongodb';
2
-
3
- import { IEntity } from '@smartsoft001/domain-core';
4
-
5
- import { Readable } from 'stream';
6
-
7
- import { MongoAttachmentRepository } from './attachment.repository';
8
-
9
- jest.mock('mongodb', () => {
10
- const mockOpenDownloadStream = jest.fn();
11
- const mockDelete = jest.fn();
12
- const mockClose = jest.fn();
13
- const mockToArray = jest.fn();
14
- const mockFind = jest.fn().mockReturnValue({
15
- toArray: mockToArray, // Return a mock cursor with toArray method
16
- });
17
-
18
- const mockWriteStream = {
19
- on: jest.fn((event, callback) => {
20
- if (event === 'finish') {
21
- setTimeout(callback, 0); // Simulate the 'finish' event after a timeout
22
- } else if (event === 'error') {
23
- // You can use this to simulate an error event in your test
24
- mockWriteStream._errorCallback = callback;
25
- }
26
- return mockWriteStream; // Return this for chaining
27
- }),
28
- pipe: jest.fn(function () {
29
- return this; // Make sure pipe is chainable
30
- }),
31
- once: jest.fn((event, callback) => {
32
- // Handle 'once' event similarly to 'on'
33
- if (event === 'finish') {
34
- setTimeout(callback, 0); // Simulate the 'finish' event after a timeout
35
- }
36
- return mockWriteStream; // Chainable
37
- }),
38
- emit: jest.fn((event, ...args) => {
39
- if (event === 'finish') {
40
- setTimeout(() => {
41
- // eslint-disable-next-line @typescript-eslint/no-empty-function
42
- mockWriteStream.on('finish', () => {
43
- // Simulate the finish event
44
- });
45
- }, 0);
46
- }
47
- return true; // Return true for emit as a valid event
48
- }),
49
- removeListener: jest.fn((event, callback) => {
50
- // Just return the mock itself for chaining
51
- return mockWriteStream;
52
- }),
53
- _errorCallback: null, // Used to simulate an error when you trigger it manually
54
- };
55
- const mockOpenUploadStreamWithId = jest.fn().mockReturnValue(mockWriteStream);
56
- const mockGridFSBucket = jest.fn().mockImplementation(() => ({
57
- delete: mockDelete,
58
- openDownloadStream: mockOpenDownloadStream,
59
- find: mockFind,
60
- openUploadStreamWithId: mockOpenUploadStreamWithId,
61
- }));
62
-
63
- const mockDb = jest.fn().mockReturnValue({
64
- GridFSBucket: mockGridFSBucket,
65
- });
66
-
67
- const mockConnect = jest.fn().mockResolvedValue({
68
- db: jest.fn(() => mockDb()),
69
- close: mockClose,
70
- });
71
-
72
- return {
73
- MongoClient: {
74
- connect: mockConnect,
75
- },
76
- GridFSBucket: mockGridFSBucket,
77
- __mockDelete: mockDelete, // Expose the mockDelete for assertions
78
- __mockClose: mockClose, // Expose the mockClose for assertions
79
- __mockOpenDownloadStream: mockOpenDownloadStream, // Expose the mockOpenDownloadStream for assertions
80
- __mockToArray: mockToArray, // Expose the mockToArray for assertions
81
- __mockWriteStream: mockWriteStream, // Expose the mockWriteStream for assertions
82
- __mockOpenUploadStreamWithId: mockOpenUploadStreamWithId, // Expose the mockOpenUploadStreamWithId for assertions
83
- };
84
- });
85
-
86
- describe('shared-mongo: MongoAttachmentRepository', () => {
87
- let model: MongoAttachmentRepository<IEntity<string>>;
88
- const mockConfig = {
89
- database: 'testDB',
90
- host: 'host',
91
- port: 4200,
92
- collection: 'testCollection',
93
- };
94
-
95
- const data = {
96
- id: 'test-id',
97
- fileName: 'test-file.txt',
98
- stream: new Readable(),
99
- mimeType: 'text/plain',
100
- encoding: 'utf8',
101
- };
102
-
103
- const options = {
104
- streamCallback: jest.fn(),
105
- };
106
-
107
- // Declare mockDelete here to access in the test scope
108
- const mongodbMock = jest.requireMock('mongodb');
109
- const mockDelete = mongodbMock.__mockDelete;
110
- const mockClose = mongodbMock.__mockClose;
111
- const mockOpenDownloadStream = mongodbMock.__mockOpenDownloadStream;
112
- const mockToArray = mongodbMock.__mockToArray;
113
- const mockWriteStream = mongodbMock.__mockWriteStream;
114
- const mockOpenUploadStreamWithId = mongodbMock.__mockOpenUploadStreamWithId;
115
- const mockStream = new Readable();
116
- mockStream._read = jest.fn();
117
-
118
- beforeEach(() => {
119
- model = new MongoAttachmentRepository<IEntity<string>>(mockConfig); // Pass required config
120
- });
121
-
122
- it('delete() should delete a file from GridFSBucket and close the client', async () => {
123
- const id = 'test-id';
124
- const mockUrl = 'mock-url';
125
- jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
126
-
127
- await model.delete(id);
128
-
129
- // Assertions
130
- expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl);
131
- expect(GridFSBucket).toHaveBeenCalledWith(expect.anything(), {
132
- bucketName: mockConfig.collection,
133
- });
134
- expect(mockDelete).toHaveBeenCalledWith(id);
135
- expect(mockClose).toHaveBeenCalled();
136
- });
137
-
138
- it('getStream() should return a readable stream when getStream is called', async () => {
139
- const id = 'test-id';
140
- const options = { start: 0, end: 100 };
141
- const mockUrl = 'mock-url';
142
- jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
143
-
144
- // Mock the return value of openDownloadStream to return a mock Readable stream
145
- const mockStream = new Readable();
146
- mockStream._read = jest.fn(); // Implement the _read method to make it a valid Readable stream
147
- mockOpenDownloadStream.mockReturnValue(mockStream);
148
-
149
- const stream = await model.getStream(id, options);
150
-
151
- // Assertions
152
- expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl); // Ensure MongoClient.connect is called with the correct URL
153
- expect(mockOpenDownloadStream).toHaveBeenCalledWith(id, options); // Check if openDownloadStream was called with the right parameters
154
- expect(stream).toBe(mockStream); // Ensure the returned stream is the one we mocked
155
- });
156
-
157
- it('getInfo() should return file info if the file exists', async () => {
158
- const id = 'test-id';
159
- const mockFileData = [
160
- {
161
- filename: 'test-file.txt',
162
- contentType: 'text/plain',
163
- length: 1024,
164
- },
165
- ];
166
- const mockUrl = 'mock-url';
167
-
168
- mockToArray.mockResolvedValueOnce(mockFileData); // Mock toArray to return mock file data
169
-
170
- const result = await model.getInfo(id);
171
-
172
- expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl); // Ensure MongoClient.connect is called with the correct URL
173
- expect(result).toEqual({
174
- fileName: 'test-file.txt',
175
- contentType: 'text/plain',
176
- length: 1024,
177
- }); // Check if the correct file info is returned
178
- });
179
-
180
- it('getInfo() should return null if no file is found', async () => {
181
- const id = 'non-existent-id';
182
-
183
- // Mock the return value of toArray to return an empty array
184
- (MongoClient.connect as jest.Mock).mockResolvedValueOnce({
185
- db: jest.fn(() => ({
186
- GridFSBucket: jest.fn().mockImplementation(() => ({
187
- find: jest.fn().mockReturnValue({
188
- toArray: jest.fn().mockResolvedValue([]), // Return mock file data
189
- }),
190
- })),
191
- })),
192
- close: jest.fn(),
193
- });
194
-
195
- const result = await model.getInfo(id);
196
-
197
- expect(result).toBeNull(); // Expect null if no file is found
198
- });
199
-
200
- it('upload() should call MongoClient.connect with correct URL', async () => {
201
- const mockUrl = 'mock-url';
202
- jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
203
- const mockStream = new Readable();
204
- mockStream._read = jest.fn();
205
- data.stream = mockStream;
206
- model.upload(data);
207
- expect(MongoClient.connect).toHaveBeenCalledWith(mockUrl);
208
- });
209
-
210
- it('upload() should resolve the upload promise', async () => {
211
- const mockUrl = 'mock-url';
212
- jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
213
- const mockStream = new Readable();
214
- mockStream._read = jest.fn();
215
- data.stream = mockStream;
216
- const result = model.upload(data);
217
- await expect(result).resolves.toBeUndefined();
218
- });
219
-
220
- it('upload() should call GridFSBucket and openUploadStreamWithId', async () => {
221
- const mockUrl = 'mock-url';
222
- jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
223
- const mockStream = new Readable();
224
- mockStream._read = jest.fn();
225
- data.stream = mockStream;
226
- await model.upload(data);
227
- expect(GridFSBucket).toHaveBeenCalledWith(expect.anything(), {
228
- bucketName: mockConfig.collection,
229
- });
230
- expect(mockOpenUploadStreamWithId).toHaveBeenCalledWith(
231
- data.id,
232
- data.fileName,
233
- {
234
- contentType: data.mimeType,
235
- },
236
- );
237
- });
238
-
239
- it('upload() should call streamCallback if provided', async () => {
240
- const mockUrl = 'mock-url';
241
- jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
242
- const mockStream = new Readable();
243
- mockStream._read = jest.fn();
244
- data.stream = mockStream;
245
- await model.upload(data, options);
246
- expect(options.streamCallback).toHaveBeenCalled();
247
- });
248
-
249
- it('upload() should call pipe of the provided data stream', async () => {
250
- const mockUrl = 'mock-url';
251
- jest.spyOn(model as any, 'getUrl').mockImplementation(() => mockUrl);
252
- const mockStream = new Readable();
253
- mockStream._read = jest.fn();
254
- mockStream.pipe = jest.fn();
255
- data.stream = mockStream;
256
- await model.upload(data);
257
- expect(mockStream.pipe).toHaveBeenCalled();
258
- });
259
- });
@@ -1,115 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { MongoClient } from 'mongodb';
3
- import * as mongo from 'mongodb';
4
-
5
- import { IEntity, IAttachmentRepository } from '@smartsoft001/domain-core';
6
-
7
- import { Readable, Stream } from 'stream';
8
-
9
- import { MongoConfig } from '../mongo.module';
10
- import { getMongoUrl } from '../mongo.utils';
11
-
12
- @Injectable()
13
- export class MongoAttachmentRepository<
14
- T extends IEntity<string>,
15
- > extends IAttachmentRepository<T> {
16
- constructor(private config: MongoConfig) {
17
- super();
18
- }
19
-
20
- async upload(
21
- data: {
22
- id: string;
23
- fileName: string;
24
- stream: Stream;
25
- mimeType: string;
26
- encoding: string;
27
- },
28
- options?: { streamCallback?: (r) => void },
29
- ): Promise<void> {
30
- const client = await MongoClient.connect(this.getUrl());
31
-
32
- return await new Promise<void>((res, rej) => {
33
- const db = client.db(this.config.database);
34
- const bucket = new mongo.GridFSBucket(db, {
35
- bucketName: this.config.collection,
36
- });
37
-
38
- const writeStream = bucket.openUploadStreamWithId(
39
- data.id as any,
40
- data.fileName,
41
- {
42
- contentType: data.mimeType,
43
- },
44
- );
45
-
46
- if (options?.streamCallback) options.streamCallback(writeStream);
47
-
48
- data.stream.pipe(writeStream as any);
49
-
50
- writeStream.on('error', (error) => {
51
- rej(error);
52
- });
53
-
54
- writeStream.on('finish', () => {
55
- res();
56
- });
57
- });
58
- }
59
-
60
- async getInfo(
61
- id: string,
62
- ): Promise<{ fileName: string; contentType: string; length: number }> {
63
- const client = await MongoClient.connect(this.getUrl());
64
-
65
- const db = client.db(this.config.database);
66
-
67
- const bucket = new mongo.GridFSBucket(db, {
68
- bucketName: this.config.collection,
69
- });
70
-
71
- const items = await bucket
72
- .find({
73
- _id: id as any,
74
- })
75
- .toArray();
76
-
77
- if (!items || items.length === 0) return null;
78
-
79
- return {
80
- fileName: items[0].filename,
81
- contentType: items[0].contentType,
82
- length: items[0].length,
83
- };
84
- }
85
-
86
- async getStream(
87
- id: string,
88
- options: { start: number; end: number } | undefined,
89
- ): Promise<Readable> {
90
- const client = await MongoClient.connect(this.getUrl());
91
-
92
- const db = client.db(this.config.database);
93
- const bucket = new mongo.GridFSBucket(db, {
94
- bucketName: this.config.collection,
95
- });
96
-
97
- return bucket.openDownloadStream(id as any, options);
98
- }
99
-
100
- async delete(id: string): Promise<void> {
101
- const client = await MongoClient.connect(this.getUrl());
102
-
103
- const db = client.db(this.config.database);
104
- const bucket = new mongo.GridFSBucket(db, {
105
- bucketName: this.config.collection,
106
- });
107
-
108
- await bucket.delete(id as any);
109
- await client.close();
110
- }
111
-
112
- private getUrl(): string {
113
- return getMongoUrl(this.config);
114
- }
115
- }
@@ -1,28 +0,0 @@
1
- export interface IItemCreateData {
2
- id: string;
3
- type: 'create';
4
- data: any;
5
- }
6
-
7
- export interface IItemUpdateData {
8
- id: string;
9
- type: 'update';
10
- data: {
11
- removedFields: Array<string>;
12
- updatedFields: {
13
- [key: string]: any;
14
- };
15
- };
16
- }
17
-
18
- export interface IItemDeleteData {
19
- id: string;
20
- type: 'delete';
21
- }
22
-
23
- export type ItemChangedData =
24
- | IItemCreateData
25
- | IItemUpdateData
26
- | IItemDeleteData;
27
-
28
- export type ItemChangedDataType = 'create' | 'update' | 'delete';