@smartsoft001/mongo 2.76.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.
- package/index.cjs +722 -0
- package/package.json +23 -3
- package/src/lib/mongo.config.d.ts +10 -0
- package/src/lib/mongo.module.d.ts +7 -0
- package/src/lib/mongo.unitofwork.d.ts +12 -0
- package/src/lib/mongo.utils.d.ts +2 -0
- package/src/lib/repositories/attachment.repository.d.ts +27 -0
- package/src/lib/repositories/interfaces.d.ts +21 -0
- package/src/lib/repositories/item.repository.d.ts +51 -0
- package/.eslintrc +0 -13
- package/jest.config.ts +0 -27
- package/project.json +0 -48
- package/src/lib/mongo.config.ts +0 -10
- package/src/lib/mongo.module.ts +0 -32
- package/src/lib/mongo.unitofwork.spec.ts +0 -84
- package/src/lib/mongo.unitofwork.ts +0 -58
- package/src/lib/mongo.utils.spec.ts +0 -53
- package/src/lib/mongo.utils.ts +0 -17
- package/src/lib/repositories/attachment.repository.spec.ts +0 -259
- package/src/lib/repositories/attachment.repository.ts +0 -115
- package/src/lib/repositories/interfaces.ts +0 -28
- package/src/lib/repositories/item.repository.spec.ts +0 -1269
- package/src/lib/repositories/item.repository.ts +0 -582
- package/tsconfig.json +0 -13
- package/tsconfig.lib.json +0 -11
- package/tsconfig.spec.json +0 -20
- /package/src/{index.ts → index.d.ts} +0 -0
- /package/{test-setup.ts → test-setup.d.ts} +0 -0
|
@@ -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';
|