diff --git a/.eslintrc.js b/.eslintrc.js index c417de3..06567bf 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -14,7 +14,6 @@ module.exports = { root: true, env: { node: true, - jest: true, }, ignorePatterns: ['.eslintrc.js'], rules: { diff --git a/package-lock.json b/package-lock.json index f3bbed1..2641d70 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,8 @@ "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "node-fetch": "^2.7.0", + "p-queue": "^6.6.2", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, @@ -23,6 +25,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/node": "^20.3.1", + "@types/node-fetch": "^2.6.9", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", @@ -2044,6 +2047,16 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/node-fetch": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.9.tgz", + "integrity": "sha512-bQVlnMLFJ2d35DkPNjEPmd9ueO/rh5EiaZt2bhqiSarPjZIuIV6bPQVqcrEyvNo+AfTrRGVazle1tl597w3gfA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.0" + } + }, "node_modules/@types/qs": { "version": "6.9.10", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz", @@ -4173,6 +4186,11 @@ "node": ">= 0.6" } }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" + }, "node_modules/events": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", @@ -6659,6 +6677,14 @@ "node": ">=0.10.0" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6689,6 +6715,32 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", diff --git a/package.json b/package.json index 4498fb2..7568ac5 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "@nestjs/common": "^10.0.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", + "node-fetch": "^2.7.0", + "p-queue": "^6.6.2", "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1" }, @@ -34,6 +36,7 @@ "@types/express": "^4.17.17", "@types/jest": "^29.5.2", "@types/node": "^20.3.1", + "@types/node-fetch": "^2.6.9", "@types/supertest": "^2.0.12", "@typescript-eslint/eslint-plugin": "^6.0.0", "@typescript-eslint/parser": "^6.0.0", diff --git a/src/integration/movies/omdb.spec.ts b/src/integration/movies/omdb.spec.ts new file mode 100644 index 0000000..e682ff0 --- /dev/null +++ b/src/integration/movies/omdb.spec.ts @@ -0,0 +1,37 @@ +import { describe, it, expect } from '@jest/globals'; + +import { createOmdbClient } from './omdb'; + +describe('createOmdbClient', () => { + const client = createOmdbClient('68fd98ab'); + it('returns some data for the sample movie id', async () => { + const result = await client.getMovieMetadata('tt11873472'); + expect(result).toMatchObject({ + 'Actors': 'Cheryl Isheja, Elvis Ngabo, Diogène Ntarindwa', + 'Awards': expect.any(String), + 'BoxOffice': expect.any(String), + 'Country': 'Rwanda, France, Canada, United Kingdom, United States', + 'DVD': expect.any(String), + 'Director': 'Anisia Uzeyman, Saul Williams', + 'Genre': 'Musical, Sci-Fi', + 'Language': 'Kinyarwanda, Kirundi, Swahili, French, English', + 'Metascore': expect.stringMatching(/^\d+$/), + 'Plot': expect.any(String), + 'Poster': expect.stringMatching(/^http/), + 'Production': expect.any(String), + 'Rated': expect.any(String), + 'Ratings': expect.any(Array), + 'Released': '10 May 2023', + 'Response': 'True', + 'Runtime': '105 min', + 'Title': 'Neptune Frost', + 'Type': 'movie', + 'Website': expect.any(String), + 'Writer': 'Saul Williams', + 'Year': '2021', + 'imdbID': 'tt11873472', + 'imdbRating': expect.stringMatching(/^\d?\d(\.\d)?$/), + 'imdbVotes': expect.stringMatching(/^\d+$/), + }); + }, 10_000); +}); diff --git a/src/integration/movies/omdb.ts b/src/integration/movies/omdb.ts new file mode 100644 index 0000000..7cd609d --- /dev/null +++ b/src/integration/movies/omdb.ts @@ -0,0 +1,26 @@ +import fetch from 'node-fetch'; +import PQueue from 'p-queue'; + +export const createOmdbClient = (apiKey: string) => { + // Rate limit (according to readme, it's 10k per day; + // here we set it 1 per second, should be enough for the demo app goal) + const queue = new PQueue({ + interval: 1000, + intervalCap: 1, + timeout: 2000, + throwOnTimeout: true, + }); + + return { + getMovieMetadata: async (movieId: string) => + queue.add(async () => { + const url = new URL('https://www.omdbapi.com/'); + url.searchParams.append('i', movieId); + url.searchParams.append('apikey', apiKey); + url.searchParams.append('plot', 'full'); + const response = await fetch(url); + const body = (await response.json()) as unknown; + return body; + }), + }; +};