diff --git a/package.json b/package.json index 82f8c440..16196084 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,7 @@ "typescript": "^3.8.3", "uuid": "^8.3.0", "winston": "^3.2.1", - "yup": "^0.28.5", - "@types/redis": "^2.8.28", + "yup": "^0.28.5", "redis": "^4.7.0" }, "devDependencies": { diff --git a/workers/grouper/package.json b/workers/grouper/package.json index 6230c8e5..4b376934 100644 --- a/workers/grouper/package.json +++ b/workers/grouper/package.json @@ -10,7 +10,6 @@ "workerType": "grouper", "dependencies": { "@types/redis": "^2.8.28", - "js-levenshtein": "^1.1.6", - "redis": "^3.1.1" + "js-levenshtein": "^1.1.6" } } diff --git a/workers/grouper/src/index.ts b/workers/grouper/src/index.ts index 22a51771..d324c1bd 100644 --- a/workers/grouper/src/index.ts +++ b/workers/grouper/src/index.ts @@ -51,6 +51,8 @@ export default class GrouperWorker extends Worker { public async start(): Promise { await this.db.connect(); this.prepareCache(); + await this.redis.initialize(); + await super.start(); } @@ -61,6 +63,7 @@ export default class GrouperWorker extends Worker { await super.finish(); this.prepareCache(); await this.db.close(); + await this.redis.close(); } /** diff --git a/workers/grouper/src/redisHelper.ts b/workers/grouper/src/redisHelper.ts index 0dfd81ac..abf7ecb3 100644 --- a/workers/grouper/src/redisHelper.ts +++ b/workers/grouper/src/redisHelper.ts @@ -1,5 +1,5 @@ import HawkCatcher from '@hawk.so/nodejs'; -import redis from 'redis'; +import { createClient, RedisClientType } from 'redis'; import createLogger from '../../../lib/logger'; /** @@ -14,7 +14,7 @@ export default class RedisHelper { /** * Redis client for making queries */ - private readonly redisClient = redis.createClient({ url: process.env.REDIS_URL }); + private readonly redisClient: RedisClientType; /** * Logger instance @@ -22,6 +22,41 @@ export default class RedisHelper { */ private logger = createLogger(); + /** + * Constructor of the Redis helper class + * Initializes the Redis client and sets up error handling + */ + constructor() { + this.redisClient = createClient({ url: process.env.REDIS_URL }); + + this.redisClient.on('error', (error) => { + if (error) { + this.logger.error(error); + HawkCatcher.send(error); + } + }); + } + + /** + * Connect to redis client + */ + public async initialize(): Promise { + try { + await this.redisClient.connect(); + } catch (error) { + console.error('Error connecting to redis', error); + } + } + + /** + * Close redis client + */ + public async close(): Promise { + if (this.redisClient.isOpen) { + await this.redisClient.quit(); + } + } + /** * Checks if a lock exists on the given group hash and identifier pair. If it does not exist, creates a lock. * Returns true if lock exists @@ -29,12 +64,20 @@ export default class RedisHelper { * @param groupHash - event group hash * @param userId - event user id */ - public checkOrSetEventLock(groupHash: string, userId: string): Promise { - return new Promise((resolve, reject) => { - const callback = this.createCallback(resolve, reject); + public async checkOrSetEventLock(groupHash: string, userId: string): Promise { + const result = await this.redisClient.set( + `${groupHash}:${userId}`, + '1', + { + EX: RedisHelper.LOCK_TTL, + NX: true, + } as const + ); - this.redisClient.set(`${groupHash}:${userId}`, '1', 'EX', RedisHelper.LOCK_TTL, 'NX', callback); - }); + /** + * Result would be null if lock already exists, false otherwise + */ + return result === null; } /** diff --git a/workers/grouper/tests/index.test.ts b/workers/grouper/tests/index.test.ts index 36203a35..9ff9dd6b 100644 --- a/workers/grouper/tests/index.test.ts +++ b/workers/grouper/tests/index.test.ts @@ -1,7 +1,7 @@ import '../../../env-test'; import GrouperWorker from '../src'; import { GroupWorkerTask } from '../types/group-worker-task'; -import redis from 'redis'; +import { createClient, RedisClientType } from 'redis'; import { Collection, MongoClient } from 'mongodb'; import { EventAddons, EventDataAccepted } from '@hawk.so/types'; @@ -74,14 +74,16 @@ function generateTask(event: Partial> = undefined } describe('GrouperWorker', () => { - const worker = new GrouperWorker(); let connection: MongoClient; let eventsCollection: Collection; let dailyEventsCollection: Collection; let repetitionsCollection: Collection; - let redisClient; + let redisClient: RedisClientType; + let worker: GrouperWorker; beforeAll(async () => { + worker = new GrouperWorker(); + await worker.start(); connection = await MongoClient.connect(process.env.MONGO_EVENTS_DATABASE_URI, { useNewUrlParser: true, @@ -90,7 +92,10 @@ describe('GrouperWorker', () => { eventsCollection = connection.db().collection('events:' + projectIdMock); dailyEventsCollection = connection.db().collection('dailyEvents:' + projectIdMock); repetitionsCollection = connection.db().collection('repetitions:' + projectIdMock); - redisClient = redis.createClient({ url: process.env.REDIS_URL }); + + redisClient = createClient({ url: process.env.REDIS_URL }); + await redisClient.connect(); + jest.resetAllMocks(); }); @@ -104,8 +109,8 @@ describe('GrouperWorker', () => { await repetitionsCollection.deleteMany({}); }); - afterEach((done) => { - redisClient.flushall(done); + afterEach(async () => { + await redisClient.flushAll(); }); describe('Saving events', () => { @@ -299,6 +304,7 @@ describe('GrouperWorker', () => { }); afterAll(async () => { + await redisClient.quit(); await worker.finish(); await connection.close(); }); diff --git a/workers/limiter/package.json b/workers/limiter/package.json index 6a5a5bd2..7491ecc1 100644 --- a/workers/limiter/package.json +++ b/workers/limiter/package.json @@ -10,7 +10,6 @@ "workerType": "cron-tasks/limiter", "dependencies": { "@types/redis": "^2.8.28", - "axios": "^0.21.2", - "redis": "^3.1.1" + "axios": "^0.21.2" } } diff --git a/workers/limiter/src/index.ts b/workers/limiter/src/index.ts index 195cd886..b3452145 100644 --- a/workers/limiter/src/index.ts +++ b/workers/limiter/src/index.ts @@ -81,6 +81,9 @@ export default class LimiterWorker extends Worker { this.projectsCollection = accountDbConnection.collection('projects'); this.workspacesCollection = accountDbConnection.collection('workspaces'); + + await this.redis.initialize(); + await super.start(); } @@ -91,6 +94,7 @@ export default class LimiterWorker extends Worker { await super.finish(); await this.eventsDb.close(); await this.accountsDb.close(); + await this.redis.close(); } /** diff --git a/workers/limiter/src/redisHelper.ts b/workers/limiter/src/redisHelper.ts index 9e06f769..dd8ddb51 100644 --- a/workers/limiter/src/redisHelper.ts +++ b/workers/limiter/src/redisHelper.ts @@ -1,5 +1,5 @@ import HawkCatcher from '@hawk.so/nodejs'; -import redis from 'redis'; +import { createClient, RedisClientType } from 'redis'; import createLogger from '../../../lib/logger'; /** @@ -9,7 +9,8 @@ export default class RedisHelper { /** * Redis client for making queries */ - private readonly redisClient = redis.createClient({ url: process.env.REDIS_URL }); + // private readonly redisClient = redis.createClient({ url: process.env.REDIS_URL }); + private readonly redisClient: RedisClientType; /** * Logger instance @@ -22,6 +23,35 @@ export default class RedisHelper { */ private readonly redisDisabledProjectsKey = 'DisabledProjectsSet'; + /** + * Constructor of the Redis helper class + * Initializes the Redis client and sets up error handling + */ + constructor() { + this.redisClient = createClient({ url: process.env.REDIS_URL }); + + this.redisClient.on('error', (error) => { + this.logger.error(error); + HawkCatcher.send(error); + }); + } + + /** + * Connect to redis client + */ + public async initialize(): Promise { + await this.redisClient.connect(); + } + + /** + * Close redis client + */ + public async close(): Promise { + if (this.redisClient.isOpen) { + await this.redisClient.quit(); + } + } + /** * Saves banned project ids to redis * If there is no projects, then previous data in Redis will be erased @@ -33,12 +63,26 @@ export default class RedisHelper { const callback = this.createCallback(resolve, reject); if (projectIdsToBan.length) { - this.redisClient.multi() - .del(this.redisDisabledProjectsKey) - .sadd(this.redisDisabledProjectsKey, projectIdsToBan) - .exec(callback); + const pipeline = this.redisClient.multi(); + + pipeline.del(this.redisDisabledProjectsKey); + + pipeline.sAdd(this.redisDisabledProjectsKey, projectIdsToBan); + + try { + pipeline.exec(); + callback(null); + } catch (err) { + callback(err); + } } else { - this.redisClient.del(this.redisDisabledProjectsKey, callback); + this.redisClient.del(this.redisDisabledProjectsKey) + .then(() => { + callback(null); + }) + .catch((err) => { + callback(err); + }); } }); } @@ -53,7 +97,9 @@ export default class RedisHelper { const callback = this.createCallback(resolve, reject); if (projectIds.length) { - this.redisClient.sadd(this.redisDisabledProjectsKey, projectIds, callback); + this.redisClient.sAdd(this.redisDisabledProjectsKey, projectIds) + .then(() => callback(null)) + .catch((err) => callback(err)); } else { resolve(); } @@ -70,7 +116,9 @@ export default class RedisHelper { const callback = this.createCallback(resolve, reject); if (projectIds.length) { - this.redisClient.srem(this.redisDisabledProjectsKey, projectIds, callback); + this.redisClient.sRem(this.redisDisabledProjectsKey, projectIds) + .then(() => callback(null)) + .catch((err) => callback(err)); } else { resolve(); } diff --git a/workers/limiter/tests/index.test.ts b/workers/limiter/tests/index.test.ts index 64e80819..810a710e 100644 --- a/workers/limiter/tests/index.test.ts +++ b/workers/limiter/tests/index.test.ts @@ -2,7 +2,7 @@ import { Collection, Db, MongoClient, ObjectId } from 'mongodb'; import '../../../env-test'; import { GroupedEventDBScheme, PlanDBScheme, ProjectDBScheme, WorkspaceDBScheme } from '@hawk.so/types'; import LimiterWorker from '../src'; -import redis from 'redis'; +import { createClient } from 'redis'; import { mockedPlans } from './plans.mock'; import axios from 'axios'; import { mocked } from 'ts-jest/utils'; @@ -128,7 +128,8 @@ describe('Limiter worker', () => { projectCollection = db.collection('projects'); workspaceCollection = db.collection('workspaces'); planCollection = db.collection('plans'); - redisClient = redis.createClient({ url: process.env.REDIS_URL }); + redisClient = createClient({ url: process.env.REDIS_URL }); + await redisClient.connect(); /** * Insert mocked plans for using in tests @@ -225,11 +226,10 @@ describe('Limiter worker', () => { * * Gets all members of set with key 'DisabledProjectsSet' from Redis */ - redisClient.smembers('DisabledProjectsSet', (err, result) => { - expect(err).toBeNull(); - expect(result).toContain(project._id.toString()); - done(); - }); + const result = await redisClient.sMembers('DisabledProjectsSet'); + + expect(result).toContain(project._id.toString()); + done(); }); test('Should not ban project if it does not reach the limit', async (done) => { @@ -265,15 +265,10 @@ describe('Limiter worker', () => { * * Gets all members of set with key 'DisabledProjectsSet' from Redis */ - redisClient.smembers('DisabledProjectsSet', (err, result) => { - expect(err).toBeNull(); - - /** - * Redis shouldn't contain id of project 'Test project #2' from 'Test workspace #2' - */ - expect(result).not.toContain(project._id.toString()); - done(); - }); + const result = await redisClient.sMembers('DisabledProjectsSet'); + + expect(result).not.toContain(project._id.toString()); + done(); }); test('Should send a report with collected data', async () => { @@ -344,11 +339,10 @@ describe('Limiter worker', () => { * * Gets all members of set with key 'DisabledProjectsSet' from Redis */ - redisClient.smembers('DisabledProjectsSet', (err, result) => { - expect(err).toBeNull(); - expect(result).toContain(project._id.toString()); - done(); - }); + const result = await redisClient.sMembers('DisabledProjectsSet'); + + expect(result).toContain(project._id.toString()); + done(); }); }); @@ -405,15 +399,13 @@ describe('Limiter worker', () => { /** * Gets all members of set with key 'DisabledProjectsSet' from Redis */ - redisClient.smembers('DisabledProjectsSet', (err, result) => { - expect(err).toBeNull(); - - /** - * Redis shouldn't contain id of project 'Test project #2' from 'Test workspace #2' - */ - expect(result).not.toContain(project._id.toString()); - done(); - }); + const result = await redisClient.sMembers('DisabledProjectsSet'); + + /** + * Redis shouldn't contain id of project 'Test project #2' from 'Test workspace #2' + */ + expect(result).not.toContain(project._id.toString()); + done(); }); test('Should block workspace if the number of events exceed the limit', async (done) => { @@ -468,15 +460,11 @@ describe('Limiter worker', () => { /** * Gets all members of set with key 'DisabledProjectsSet' from Redis */ - redisClient.smembers('DisabledProjectsSet', (err, result) => { - expect(err).toBeNull(); - - /** - * Redis shouldn't contain id of the mocked project - */ - expect(result).toContain(project._id.toString()); - done(); - }); + + const result = await redisClient.sMembers('DisabledProjectsSet'); + + expect(result).toContain(project._id.toString()); + done(); }); test('Should correctly work if projects count equals 0', async () => { @@ -504,5 +492,6 @@ describe('Limiter worker', () => { afterAll(async () => { await connection.close(); + await redisClient.quit(); }); }); diff --git a/yarn.lock b/yarn.lock index fe73d119..98c71c10 100644 --- a/yarn.lock +++ b/yarn.lock @@ -608,6 +608,40 @@ resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.1.0.tgz#cba454c05ec201bd5547aaf55286d44682ac8eb5" integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g== +"@redis/bloom@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@redis/bloom/-/bloom-1.2.0.tgz#d3fd6d3c0af3ef92f26767b56414a370c7b63b71" + integrity sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg== + +"@redis/client@1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.6.0.tgz#dcf4ae1319763db6fdddd6de7f0af68a352c30ea" + integrity sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg== + dependencies: + cluster-key-slot "1.1.2" + generic-pool "3.9.0" + yallist "4.0.0" + +"@redis/graph@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@redis/graph/-/graph-1.1.1.tgz#8c10df2df7f7d02741866751764031a957a170ea" + integrity sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw== + +"@redis/json@1.0.7": + version "1.0.7" + resolved "https://registry.yarnpkg.com/@redis/json/-/json-1.0.7.tgz#016257fcd933c4cbcb9c49cde8a0961375c6893b" + integrity sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ== + +"@redis/search@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@redis/search/-/search-1.2.0.tgz#50976fd3f31168f585666f7922dde111c74567b8" + integrity sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw== + +"@redis/time-series@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@redis/time-series/-/time-series-1.1.0.tgz#cba454c05ec201bd5547aaf55286d44682ac8eb5" + integrity sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g== + "@sentry/core@^8.45.1": version "8.47.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.47.0.tgz#e811444552f7a91b5de573875a318a6cd4e802f8" @@ -715,6 +749,13 @@ dependencies: "@types/node" "*" +"@types/dockerode@^2.5.34": + version "2.5.34" + resolved "https://registry.yarnpkg.com/@types/dockerode/-/dockerode-2.5.34.tgz#9adb884f7cc6c012a6eb4b2ad794cc5d01439959" + integrity sha512-LcbLGcvcBwBAvjH9UrUI+4qotY+A5WCer5r43DR5XHv2ZIEByNXFdPLo1XxR+v/BjkGjlggW8qUiXuVEhqfkpA== + dependencies: + "@types/node" "*" + "@types/dotenv@^8.2.0": version "8.2.0" resolved "https://registry.yarnpkg.com/@types/dotenv/-/dotenv-8.2.0.tgz#5cd64710c3c98e82d9d15844375a33bf1b45d053" @@ -1226,6 +1267,11 @@ any-promise@^1.1.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== +any-promise@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" + integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== + anymatch@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" @@ -1334,6 +1380,13 @@ asn1@^0.2.6: dependencies: safer-buffer "~2.1.0" +asn1@^0.2.6: + version "0.2.6" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" + integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== + dependencies: + safer-buffer "~2.1.0" + asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -1500,10 +1553,12 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== + integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" @@ -1782,6 +1837,11 @@ buildcheck@~0.0.6: resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== +buildcheck@~0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.6.tgz#89aa6e417cfd1e2196e3f8fe915eb709d2fe4238" + integrity sha512-8f9ZJCUXyT1M35Jx7MkBgmBMo3oHTTBIPLiY9xyL0pl3T5RwcPEY8cUHr5LBNfu/fk6c2T4DJZuVM/8ZZT2D2A== + builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" @@ -1792,6 +1852,11 @@ byline@^5.0.0: resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== +byline@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" + integrity sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q== + cacache@^12.0.2: version "12.0.4" resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" @@ -2043,6 +2108,11 @@ cluster-key-slot@1.1.2: resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== +cluster-key-slot@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz#88ddaa46906e303b5de30d3153b7d9fe0a0c19ac" + integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2248,6 +2318,14 @@ cpu-features@~0.0.10: buildcheck "~0.0.6" nan "^2.19.0" +cpu-features@~0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" + integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== + dependencies: + buildcheck "~0.0.6" + nan "^2.19.0" + create-ecdh@^4.0.0: version "4.0.4" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" @@ -2484,7 +2562,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -denque@^1.4.1, denque@^1.5.0: +denque@^1.4.1: version "1.5.1" resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.1.tgz#07f670e29c9a78f8faecb2566a1e2c11929c5cbf" integrity sha512-XwE+iZ4D6ZUB7mfYRMb5wByE8L74HCn30FBN7sWnXksWc1LO1bPDl67pBR9o/kC4z/xSNAwkMYcGgqDV3BE3Hw== @@ -2564,6 +2642,32 @@ dockerode@^3.2.1: docker-modem "^3.0.0" tar-fs "~2.0.1" +docker-compose@^0.23.5: + version "0.23.19" + resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.19.tgz#9947726e2fe67bdfa9e8efe1ff15aa0de2e10eb8" + integrity sha512-v5vNLIdUqwj4my80wxFDkNH+4S85zsRuH29SO7dCWVWPCMt/ohZBsGN6g6KXWifT0pzQ7uOxqEKCYCDPJ8Vz4g== + dependencies: + yaml "^1.10.2" + +docker-modem@^3.0.0: + version "3.0.8" + resolved "https://registry.yarnpkg.com/docker-modem/-/docker-modem-3.0.8.tgz#ef62c8bdff6e8a7d12f0160988c295ea8705e77a" + integrity sha512-f0ReSURdM3pcKPNS30mxOHSbaFLcknGmQjwSfmbcdOw1XWKXVhukM3NJHhr7NpY9BIyyWQb0EBo3KQvvuU5egQ== + dependencies: + debug "^4.1.1" + readable-stream "^3.5.0" + split-ca "^1.0.1" + ssh2 "^1.11.0" + +dockerode@^3.2.1: + version "3.3.5" + resolved "https://registry.yarnpkg.com/dockerode/-/dockerode-3.3.5.tgz#7ae3f40f2bec53ae5e9a741ce655fff459745629" + integrity sha512-/0YNa3ZDNeLr/tSckmD69+Gq+qVNhvKfAHNeZJBnp7EOP6RGKV8ORrJHkUn20So5wU+xxT7+1n5u8PjHbfjbSA== + dependencies: + "@balena/dockerignore" "^1.0.2" + docker-modem "^3.0.0" + tar-fs "~2.0.1" + doctrine@1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" @@ -3435,6 +3539,11 @@ generic-pool@3.9.0: resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== +generic-pool@3.9.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/generic-pool/-/generic-pool-3.9.0.tgz#36f4a678e963f4fdb8707eab050823abc4e8f5e4" + integrity sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g== + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -3549,6 +3658,18 @@ glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.1.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + global-dirs@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686" @@ -5238,6 +5359,13 @@ minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" +minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -5287,6 +5415,11 @@ mkdirp-classic@^0.5.2: resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== +mkdirp-classic@^0.5.2: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" + integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== + mkdirp@0.5.5, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -5446,6 +5579,11 @@ nan@^2.19.0, nan@^2.20.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== +nan@^2.19.0, nan@^2.20.0: + version "2.22.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.22.0.tgz#31bc433fc33213c97bad36404bb68063de604de3" + integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== + nanoid@^3.1.31: version "3.1.31" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.31.tgz#f5b58a1ce1b7604da5f0605757840598d8974dc6" @@ -5504,6 +5642,11 @@ node-duration@^1.0.4: resolved "https://registry.yarnpkg.com/node-duration/-/node-duration-1.0.4.tgz#3e94ecc0e473691c89c4560074503362071cecac" integrity sha512-eUXYNSY7DL53vqfTosggWkvyIW3bhAcqBDIlolgNYlZhianXTrCL50rlUJWD1eRqkIxMppXTfiFbp+9SjpPrgA== +node-duration@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/node-duration/-/node-duration-1.0.4.tgz#3e94ecc0e473691c89c4560074503362071cecac" + integrity sha512-eUXYNSY7DL53vqfTosggWkvyIW3bhAcqBDIlolgNYlZhianXTrCL50rlUJWD1eRqkIxMppXTfiFbp+9SjpPrgA== + node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" @@ -6411,6 +6554,7 @@ read-pkg@^5.2.0: isarray "0.0.1" string_decoder "~0.10.x" +readable-stream@^3.1.1, readable-stream@^3.5.0: readable-stream@^3.1.1, readable-stream@^3.5.0: version "3.6.2" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" @@ -7054,6 +7198,11 @@ split-ca@^1.0.1: resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== +split-ca@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/split-ca/-/split-ca-1.0.1.tgz#6c83aff3692fa61256e0cd197e05e9de157691a6" + integrity sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ== + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -7077,6 +7226,17 @@ ssh2@^1.11.0: cpu-features "~0.0.10" nan "^2.20.0" +ssh2@^1.11.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/ssh2/-/ssh2-1.16.0.tgz#79221d40cbf4d03d07fe881149de0a9de928c9f0" + integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg== + dependencies: + asn1 "^0.2.6" + bcrypt-pbkdf "^1.0.2" + optionalDependencies: + cpu-features "~0.0.10" + nan "^2.20.0" + sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" @@ -7163,6 +7323,13 @@ stream-to-array@^2.3.0: dependencies: any-promise "^1.1.0" +stream-to-array@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/stream-to-array/-/stream-to-array-2.3.0.tgz#bbf6b39f5f43ec30bc71babcb37557acecf34353" + integrity sha512-UsZtOYEn4tWU2RGLOXr/o/xjRBftZRlG3dEWoaHr8j4GuypJ3isitGbVyjQKAuMu+xbiop8q224TjiZWc4XTZA== + dependencies: + any-promise "^1.1.0" + string-length@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-3.1.0.tgz#107ef8c23456e187a8abd4a61162ff4ac6e25837" @@ -7375,6 +7542,27 @@ tar-fs@~2.0.1: pump "^3.0.0" tar-stream "^2.0.0" +tar-stream@^2.0.0, tar-stream@^2.1.4: +tar-fs@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" + integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.1.4" + +tar-fs@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.1.tgz#e44086c1c60d31a4f0cf893b1c4e155dabfae9e2" + integrity sha512-6tzWDMeroL87uF/+lin46k+Q+46rAJ0SyPGz7OW7wTgblI273hsBqk2C1j0/xNadNLKDTUL9BukSjB7cwgmlPA== + dependencies: + chownr "^1.1.1" + mkdirp-classic "^0.5.2" + pump "^3.0.0" + tar-stream "^2.0.0" + tar-stream@^2.0.0, tar-stream@^2.1.4: version "2.2.0" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" @@ -7463,6 +7651,22 @@ testcontainers@^3.0.0: stream-to-array "^2.3.0" tar-fs "^2.1.0" +testcontainers@^3.0.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/testcontainers/-/testcontainers-3.5.0.tgz#da8a3fa53f1c6d106ccff7e0f71bf86d358c2589" + integrity sha512-iigGuzBDzOmTFlxfaTo1JO7EGMs4Dgf+XHd98XeEwp8J+WzOdkJfsSL5A8fdLSGQA5GvVAdxLcUfT4a32z4rbg== + dependencies: + "@types/dockerode" "^2.5.34" + byline "^5.0.0" + debug "^4.1.1" + docker-compose "^0.23.5" + dockerode "^3.2.1" + get-port "^5.1.1" + glob "^7.1.6" + node-duration "^1.0.4" + stream-to-array "^2.3.0" + tar-fs "^2.1.0" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -8207,6 +8411,11 @@ yallist@4.0.0, yallist@^4.0.0: resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yallist@4.0.0, yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" @@ -8217,6 +8426,10 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.1.1: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yaml@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" + integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== yaml@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"