Added base64 encoding for images
Reviewed-on: #1 Co-authored-by: Sugui <anaisusg@gmail.com> Co-committed-by: Sugui <anaisusg@gmail.com>
This commit is contained in:
parent
0472cf24f1
commit
5b0701fc93
3
TODO.md
3
TODO.md
|
@ -1,3 +1,4 @@
|
||||||
- Parametrize bot API url in env variable
|
- Parametrize bot API url in env variable
|
||||||
- Encode images in base64
|
- Encode images in base64 with proper prefix "data:image/png;base64,<encoding>"
|
||||||
- Parametrize tags
|
- Parametrize tags
|
||||||
|
- Generalize the ImageService test with faker
|
|
@ -21,12 +21,24 @@ class ImageService {
|
||||||
const botResponse = await BotApiService.getAll();
|
const botResponse = await BotApiService.getAll();
|
||||||
const imagesUrls = botResponse.images.map(image => image.url);
|
const imagesUrls = botResponse.images.map(image => image.url);
|
||||||
|
|
||||||
const validPosts = posts
|
const validPosts = Promise.all(posts
|
||||||
.filter(post => !imagesUrls.some(url => url === post.url))
|
.filter(post => !imagesUrls.some(url => url === post.url))
|
||||||
.map(post => ({ url: post.url, tags: post.tags, content: "TODO" }));
|
.map(async (post): Promise<Image> => {
|
||||||
|
const imageData = await this.urlToImageData(post.url);
|
||||||
|
const encodedImage = this.imageToBase64(imageData);
|
||||||
|
return { url: post.url, tags: post.tags, content: encodedImage };
|
||||||
|
}));
|
||||||
|
|
||||||
return validPosts;
|
return validPosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async urlToImageData(url: string): Promise<string> {
|
||||||
|
return await (await fetch(url)).text();
|
||||||
|
}
|
||||||
|
|
||||||
|
private imageToBase64(imageData: string): string {
|
||||||
|
return Buffer.from(imageData, "binary").toString("base64");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ImageService();
|
export default new ImageService();
|
|
@ -1,41 +0,0 @@
|
||||||
import { describe, expect, it, mock } from "bun:test";
|
|
||||||
import Image from "../src/types/Image";
|
|
||||||
import ImageService from "../src/services/ImageService";
|
|
||||||
import GelbooruApiResponse from "../src/types/GelbooruServiceResponse";
|
|
||||||
import { BotApiResponse } from "../src/types/BotApiResponse";
|
|
||||||
|
|
||||||
describe("endpoint gets a non repeated image", () => {
|
|
||||||
it("should return an image that is not in the response of the /images endpoint of the bot API", async () => {
|
|
||||||
const REPEATED_URL = "image.com/1";
|
|
||||||
const UNIQUE_URL = "image.com/2";
|
|
||||||
|
|
||||||
mock.module("../src/services/GelbooruApiService", () => {
|
|
||||||
let alreadyCalled = false;
|
|
||||||
return {
|
|
||||||
default: {
|
|
||||||
get: (): GelbooruApiResponse => {
|
|
||||||
if (alreadyCalled) {
|
|
||||||
return { posts: [{ url: UNIQUE_URL, tags: [] }] };
|
|
||||||
} else {
|
|
||||||
alreadyCalled = true;
|
|
||||||
return { posts: [{ url: REPEATED_URL, tags: [] }] };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.module("../src/services/BotApiService", () => ({
|
|
||||||
default: {
|
|
||||||
getAll: (): BotApiResponse => ({
|
|
||||||
images: [
|
|
||||||
{ _id: "0", url: REPEATED_URL, status: "consumed", tags: ["pokemon", "computer"], "__v": 0 }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
const image: Image = await ImageService.get();
|
|
||||||
expect(image.url).not.toBe(REPEATED_URL);
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
import { describe, expect, it, mock, spyOn } from "bun:test";
|
||||||
|
import Image from "../../src/types/Image";
|
||||||
|
import ImageService from "../../src/services/ImageService";
|
||||||
|
import GelbooruApiResponse from "../../src/types/GelbooruServiceResponse";
|
||||||
|
import { BotApiResponse } from "../../src/types/BotApiResponse";
|
||||||
|
import fs from "node:fs";
|
||||||
|
|
||||||
|
describe("endpoint gets a non repeated image", () => {
|
||||||
|
it("should return an image that is not in the response of the /images endpoint of the bot API", async () => {
|
||||||
|
const REPEATED_URL = "https://fastly.picsum.photos/id/1/10/20.jpg?hmac=gY6PvUXFacKfYpBpTTVcNLxumpyMmoCamM-J5DOPwNc";
|
||||||
|
const UNIQUE_URL = "https://fastly.picsum.photos/id/2/10/20.jpg?hmac=zy6lz21CuRIstr9ETx9h5AuoH50s_L2uIEct3dROpY8";
|
||||||
|
|
||||||
|
mock.module("../../src/services/GelbooruApiService", () => {
|
||||||
|
let alreadyCalled = false;
|
||||||
|
return {
|
||||||
|
default: {
|
||||||
|
get: (): GelbooruApiResponse => {
|
||||||
|
if (alreadyCalled) {
|
||||||
|
return { posts: [{ url: UNIQUE_URL, tags: [] }] };
|
||||||
|
} else {
|
||||||
|
alreadyCalled = true;
|
||||||
|
return { posts: [{ url: REPEATED_URL, tags: [] }] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mock.module("../../src/services/BotApiService", () => ({
|
||||||
|
default: {
|
||||||
|
getAll: (): BotApiResponse => ({
|
||||||
|
images: [
|
||||||
|
{ _id: "0", url: REPEATED_URL, status: "consumed", tags: ["pokemon", "computer"], "__v": 0 }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const image: Image = await ImageService.get();
|
||||||
|
expect(image.url).not.toBe(REPEATED_URL);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("imageToBase64 works as expected", () => {
|
||||||
|
it("should encode image to base64 properly", () => {
|
||||||
|
const imageData = fs.readFileSync('./test/ImageService/testImage.jpg');
|
||||||
|
const imageToBase64Spy = spyOn(ImageService as any, 'imageToBase64');
|
||||||
|
|
||||||
|
const actualEncoding = imageToBase64Spy.call(ImageService, imageData);
|
||||||
|
const expectedEncoding = fs.readFileSync('./test/ImageService/testImage.b64', "base64").toString();
|
||||||
|
|
||||||
|
expect(actualEncoding).toBe(expectedEncoding);
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1 @@
|
||||||
|
/9j/4QDeRXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAVgAAABsBBQABAAAAXgAAACgBAwABAAAAAgAAABMCAwABAAAAAQAAAGmHBAABAAAAZgAAAAAAAABIAAAAAQAAAEgAAAABAAAABwAAkAcABAAAADAyMTABkQcABAAAAAECAwCGkgcAFQAAAMAAAAAAoAcABAAAADAxMDABoAMAAQAAAP//AAACoAQAAQAAAAIAAAADoAQAAQAAAAIAAAAAAAAAQVNDSUkAAABQaWNzdW0gSUQ6IDEwAP/bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAkLicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/CABEIAAIAAgMBIgACEQEDEQH/xAAVAAEBAAAAAAAAAAAAAAAAAAAABf/EABUBAQEAAAAAAAAAAAAAAAAAAAIE/9oADAMBAAIQAxAAAAGIHP8A/8QAFhAAAwAAAAAAAAAAAAAAAAAAAAMT/9oACAEBAAEFAqsP/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwF//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAIzkf/aAAgBAQAGPwKjaf/EABUQAQEAAAAAAAAAAAAAAAAAAADx/9oACAEBAAE/Ibt//9oADAMBAAIAAwAAABD3/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPxB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPxB//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAERgfD/2gAIAQEAAT8QUCyWf//Z
|
Binary file not shown.
After Width: | Height: | Size: 744 B |
Loading…
Reference in New Issue