Skip to content

Commit 369f09b

Browse files
authored
Merge pull request #252 from pacyL2K19/feat/support-url-in-nearimage
feat: add the support of URLs for the `nearImage()` method
2 parents b9bdb0c + df5e157 commit 369f09b

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed

src/utils/base64.test.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { httpClient } from '../connection/http.js';
2+
import { downloadImageFromURLAsBase64 } from './base64.js';
3+
4+
jest.mock('../connection/http.js');
5+
6+
describe('downloadImageFromURLAsBase64()', () => {
7+
const mockHttpClient = {
8+
externalGet: jest.fn(),
9+
};
10+
11+
beforeEach(() => {
12+
jest.clearAllMocks();
13+
(httpClient as jest.Mock).mockReturnValue(mockHttpClient);
14+
});
15+
16+
it('should convert a downloaded image to base64', async () => {
17+
const mockUrl = 'https://example.com/image.jpg';
18+
const mockImageData = Buffer.from('image binary data');
19+
20+
mockHttpClient.externalGet.mockResolvedValue(mockImageData);
21+
22+
const result = await downloadImageFromURLAsBase64(mockUrl);
23+
24+
expect(result).toBe(mockImageData.toString('base64'));
25+
expect(httpClient).toHaveBeenCalledWith({ headers: { 'Content-Type': 'image/*' }, host: '' });
26+
expect(mockHttpClient.externalGet).toHaveBeenCalledWith(mockUrl);
27+
});
28+
29+
it('should throw an error if the URL is invalid', async () => {
30+
const invalidUrl = 'invalid-url';
31+
32+
await expect(downloadImageFromURLAsBase64(invalidUrl)).rejects.toThrow('Invalid URL');
33+
});
34+
35+
it('should throw an error if the image download fails', async () => {
36+
const mockUrl = 'https://example.com/image.jpg';
37+
38+
mockHttpClient.externalGet.mockRejectedValue(new Error('Network error'));
39+
40+
await expect(downloadImageFromURLAsBase64(mockUrl)).rejects.toThrow('Failed to download image from URL');
41+
expect(httpClient).toHaveBeenCalledWith({ headers: { 'Content-Type': 'image/*' }, host: '' });
42+
expect(mockHttpClient.externalGet).toHaveBeenCalledWith(mockUrl);
43+
});
44+
45+
it('should handle empty response data gracefully', async () => {
46+
const mockUrl = 'https://example.com/image.jpg';
47+
48+
mockHttpClient.externalGet.mockResolvedValue(Buffer.alloc(0));
49+
50+
const result = await downloadImageFromURLAsBase64(mockUrl);
51+
52+
expect(result).toBe('');
53+
expect(httpClient).toHaveBeenCalledWith({ headers: { 'Content-Type': 'image/*' }, host: '' });
54+
expect(mockHttpClient.externalGet).toHaveBeenCalledWith(mockUrl);
55+
});
56+
57+
it('should throw an error if the response is not a buffer', async () => {
58+
const mockUrl = 'wrong-url.com';
59+
60+
mockHttpClient.externalGet.mockResolvedValue('not a buffer');
61+
62+
await expect(downloadImageFromURLAsBase64(mockUrl)).rejects.toThrow('Invalid URL');
63+
});
64+
});

src/utils/base64.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import fs from 'fs';
2+
import { httpClient } from '../connection/http.js';
23

34
const isFilePromise = (file: string | Buffer): Promise<boolean> =>
45
new Promise((resolve, reject) => {
@@ -22,6 +23,39 @@ const isFilePromise = (file: string | Buffer): Promise<boolean> =>
2223
});
2324
});
2425

26+
const isUrl = (file: string): file is string => {
27+
if (typeof file !== 'string') return false;
28+
try {
29+
const url = new URL(file);
30+
return !!url;
31+
} catch {
32+
return false;
33+
}
34+
};
35+
36+
export const downloadImageFromURLAsBase64 = async (url: string): Promise<string> => {
37+
if (!isUrl(url)) {
38+
throw new Error('Invalid URL');
39+
}
40+
41+
try {
42+
const client = httpClient({
43+
headers: { 'Content-Type': 'image/*' },
44+
host: '',
45+
});
46+
47+
const response = await client.externalGet(url);
48+
49+
if (!Buffer.isBuffer(response)) {
50+
throw new Error('Response is not a buffer');
51+
}
52+
53+
return response.toString('base64');
54+
} catch (error) {
55+
throw new Error(`Failed to download image from URL: ${url}`);
56+
}
57+
};
58+
2559
const isBuffer = (file: string | Buffer): file is Buffer => file instanceof Buffer;
2660

2761
const fileToBase64 = (file: string | Buffer): Promise<string> =>
@@ -37,14 +71,16 @@ const fileToBase64 = (file: string | Buffer): Promise<string> =>
3771
})
3872
: isBuffer(file)
3973
? Promise.resolve(file.toString('base64'))
74+
: isUrl(file)
75+
? downloadImageFromURLAsBase64(file)
4076
: Promise.resolve(file)
4177
);
4278

4379
/**
4480
* This function converts a file buffer into a base64 string so that it can be
4581
* sent to Weaviate and stored as a media field.
4682
*
47-
* @param {string | Buffer} file The media to convert either as a base64 string, a file path string, or as a buffer. If you passed a base64 string, the function does nothing and returns the string as is.
83+
* @param {string | Buffer} file The media to convert either as a base64 string, a file path string, an url, or as a buffer. If you passed a base64 string, the function does nothing and returns the string as is.
4884
* @returns {string} The base64 string
4985
*/
5086
export const toBase64FromMedia = (media: string | Buffer): Promise<string> => fileToBase64(media);

0 commit comments

Comments
 (0)