From eceb7af96b44f832dd31b43df48ed050c8b01343 Mon Sep 17 00:00:00 2001 From: "parabol-release-bot[bot]" <150284312+parabol-release-bot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 11:36:10 -0700 Subject: [PATCH 01/18] chore(release): release v7.39.0 (#10010) Co-authored-by: parabol-release-bot[bot] <150284312+parabol-release-bot[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 15 +++++++++++++++ package.json | 2 +- packages/chronos/package.json | 4 ++-- packages/client/package.json | 2 +- packages/embedder/package.json | 2 +- packages/gql-executor/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/server/package.json | 4 ++-- 9 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 444fcf166a0..6054f981be4 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.38.11" + ".": "7.39.0" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 523db907f02..2a7739ff7b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ This project adheres to [Semantic Versioning](http://semver.org/). This CHANGELOG follows conventions [outlined here](http://keepachangelog.com/). +## [7.39.0](https://github.com/ParabolInc/parabol/compare/v7.38.11...v7.39.0) (2024-07-23) + + +### Added + +* GCal event series for Standup ([#9959](https://github.com/ParabolInc/parabol/issues/9959)) ([8a6659a](https://github.com/ParabolInc/parabol/commit/8a6659a2373a101a970b2fb2a1004c0e21c85f65)) + + +### Changed + +* Fix test ([#10013](https://github.com/ParabolInc/parabol/issues/10013)) ([23c8048](https://github.com/ParabolInc/parabol/commit/23c8048eb88916d5f2021dc0e830fd3b953452e6)) +* Move more integration GraphQL types to SDL ([#10015](https://github.com/ParabolInc/parabol/issues/10015)) ([1279971](https://github.com/ParabolInc/parabol/commit/12799719081e53fdf0976005dabf851fc2908c42)) +* Reduce Azure DevOps scope ([#9999](https://github.com/ParabolInc/parabol/issues/9999)) ([e6a3c7d](https://github.com/ParabolInc/parabol/commit/e6a3c7d12b0f4e7a3b7caa4fe62ab81764c9f577)) +* upgrade from gpt-3.5-turbo to gpt-4o-mini ([#10002](https://github.com/ParabolInc/parabol/issues/10002)) ([b816727](https://github.com/ParabolInc/parabol/commit/b816727460620637f49027a1b32e47d3a2676152)) + ## [7.38.11](https://github.com/ParabolInc/parabol/compare/v7.38.10...v7.38.11) (2024-07-19) diff --git a/package.json b/package.json index dcd82b5d9e7..7c1052a333a 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.38.11", + "version": "7.39.0", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/chronos/package.json b/packages/chronos/package.json index 729ca876e99..c71da5ef114 100644 --- a/packages/chronos/package.json +++ b/packages/chronos/package.json @@ -1,6 +1,6 @@ { "name": "chronos", - "version": "7.38.11", + "version": "7.39.0", "description": "A cron job scheduler", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/chronos#readme", @@ -25,6 +25,6 @@ }, "dependencies": { "cron": "^2.3.1", - "parabol-server": "7.38.11" + "parabol-server": "7.39.0" } } diff --git a/packages/client/package.json b/packages/client/package.json index b968a7a3998..1a7a040a775 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.38.11", + "version": "7.39.0", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/embedder/package.json b/packages/embedder/package.json index b80958ac24f..812dc94a797 100644 --- a/packages/embedder/package.json +++ b/packages/embedder/package.json @@ -1,6 +1,6 @@ { "name": "parabol-embedder", - "version": "7.38.11", + "version": "7.39.0", "description": "A service that computes embedding vectors from Parabol objects", "author": "Jordan Husney ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/embedder#readme", diff --git a/packages/gql-executor/package.json b/packages/gql-executor/package.json index 0d125435860..71a1e1dea06 100644 --- a/packages/gql-executor/package.json +++ b/packages/gql-executor/package.json @@ -1,6 +1,6 @@ { "name": "gql-executor", - "version": "7.38.11", + "version": "7.39.0", "description": "A Stateless GraphQL Executor", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/gqlExecutor#readme", @@ -27,8 +27,8 @@ }, "dependencies": { "dd-trace": "^4.2.0", - "parabol-client": "7.38.11", - "parabol-server": "7.38.11", + "parabol-client": "7.39.0", + "parabol-server": "7.39.0", "undici": "^5.26.2" } } diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index bb4a3bf0cc5..a0cb0594b08 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -2,7 +2,7 @@ "name": "integration-tests", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.38.11", + "version": "7.39.0", "description": "", "main": "index.js", "scripts": { diff --git a/packages/server/package.json b/packages/server/package.json index bef4d94ca22..ec51fbe3381 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.38.11", + "version": "7.39.0", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" @@ -124,7 +124,7 @@ "openai": "^4.24.1", "openapi-fetch": "^0.9.7", "oy-vey": "^0.12.1", - "parabol-client": "7.38.11", + "parabol-client": "7.39.0", "pg": "^8.5.1", "react": "^17.0.2", "react-dom": "^17.0.2", From 90de32f702849a92be28dff42188445947a54325 Mon Sep 17 00:00:00 2001 From: Matt Krick Date: Tue, 23 Jul 2024 16:26:54 -0700 Subject: [PATCH 02/18] chore(rethinkdb): TeamMember: Phase 2 (#9993) Signed-off-by: Matt Krick --- packages/server/__tests__/globalTeardown.ts | 1 + .../__tests__/isOrgVerified.test.ts | 1 + .../__tests__/usersCustomRedisQueries.test.ts | 3 - .../mutations/checkRethinkPgEquality.ts | 36 +++-- .../1721356124871_TeamMember-phase2.ts | 144 ++++++++++++++++++ packages/server/postgres/utils/checkEqBase.ts | 4 +- .../postgres/utils/rethinkEqualityFns.ts | 5 + .../isRequestToJoinDomainAllowed.test.ts | 1 + 8 files changed, 176 insertions(+), 19 deletions(-) create mode 100644 packages/server/postgres/migrations/1721356124871_TeamMember-phase2.ts diff --git a/packages/server/__tests__/globalTeardown.ts b/packages/server/__tests__/globalTeardown.ts index b5a3cd1633f..03744b6ffa4 100644 --- a/packages/server/__tests__/globalTeardown.ts +++ b/packages/server/__tests__/globalTeardown.ts @@ -6,6 +6,7 @@ async function teardown() { const r = await getRethink() await r.getPoolMaster()?.drain() await getKysely().destroy() + console.log('global teardown destroy') await getRedis().quit() } diff --git a/packages/server/dataloader/__tests__/isOrgVerified.test.ts b/packages/server/dataloader/__tests__/isOrgVerified.test.ts index ed37f906642..2c6957b83f4 100644 --- a/packages/server/dataloader/__tests__/isOrgVerified.test.ts +++ b/packages/server/dataloader/__tests__/isOrgVerified.test.ts @@ -68,6 +68,7 @@ afterEach(async () => { afterAll(async () => { await getKysely().destroy() + console.log('org verified destroy') }) test('Founder is billing lead', async () => { diff --git a/packages/server/dataloader/__tests__/usersCustomRedisQueries.test.ts b/packages/server/dataloader/__tests__/usersCustomRedisQueries.test.ts index 50fcc7fa77f..3d1dbb8c44e 100644 --- a/packages/server/dataloader/__tests__/usersCustomRedisQueries.test.ts +++ b/packages/server/dataloader/__tests__/usersCustomRedisQueries.test.ts @@ -5,10 +5,7 @@ import isValid from '../../graphql/isValid' import getPg from '../../postgres/getPg' afterAll(async () => { - const pg = getPg() const dataloader = getDataLoader() - - await pg.end() dataloader.dispose(true) // TODO shutdown redis to properly end test }) diff --git a/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts b/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts index 47c8215ed43..927cf3685fe 100644 --- a/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts +++ b/packages/server/graphql/private/mutations/checkRethinkPgEquality.ts @@ -2,7 +2,13 @@ import getRethink from '../../../database/rethinkDriver' import getFileStoreManager from '../../../fileStorage/getFileStoreManager' import getKysely from '../../../postgres/getKysely' import {checkRowCount, checkTableEq} from '../../../postgres/utils/checkEqBase' -import {compareRValUndefinedAsNull, defaultEqFn} from '../../../postgres/utils/rethinkEqualityFns' +import { + compareDateAlmostEqual, + compareRValUndefinedAsFalse, + compareRValUndefinedAsNull, + compareRValUndefinedAsNullAndTruncateRVal, + defaultEqFn +} from '../../../postgres/utils/rethinkEqualityFns' import {MutationResolvers} from '../resolverTypes' const handleResult = async ( @@ -27,35 +33,37 @@ const checkRethinkPgEquality: MutationResolvers['checkRethinkPgEquality'] = asyn ) => { const r = await getRethink() - if (tableName === 'OrganizationUser') { + if (tableName === 'TeamMember') { const rowCountResult = await checkRowCount(tableName) const rethinkQuery = (joinedAt: Date, id: string | number) => { return r - .table('OrganizationUser' as any) + .table('TeamMember' as any) .between([joinedAt, id], [r.maxval, r.maxval], { - index: 'joinedAtId', + index: 'updatedAtId', leftBound: 'open', rightBound: 'closed' }) - .orderBy({index: 'joinedAtId'}) as any + .orderBy({index: 'updatedAtId'}) as any } const pgQuery = async (ids: string[]) => { - return getKysely().selectFrom('OrganizationUser').selectAll().where('id', 'in', ids).execute() + return getKysely().selectFrom('TeamMember').selectAll().where('id', 'in', ids).execute() } const errors = await checkTableEq( rethinkQuery, pgQuery, { id: defaultEqFn, - suggestedTier: compareRValUndefinedAsNull, - inactive: defaultEqFn, - joinedAt: defaultEqFn, - orgId: defaultEqFn, - removedAt: defaultEqFn, - role: compareRValUndefinedAsNull, + isNotRemoved: compareRValUndefinedAsFalse, + isLead: compareRValUndefinedAsFalse, + isSpectatingPoker: compareRValUndefinedAsFalse, + email: defaultEqFn, + openDrawer: compareRValUndefinedAsNull, + picture: defaultEqFn, + preferredName: compareRValUndefinedAsNullAndTruncateRVal(100), + teamId: defaultEqFn, userId: defaultEqFn, - tier: defaultEqFn, - trialStartDate: compareRValUndefinedAsNull + createdAt: compareDateAlmostEqual, + updatedAt: compareDateAlmostEqual }, maxErrors ) diff --git a/packages/server/postgres/migrations/1721356124871_TeamMember-phase2.ts b/packages/server/postgres/migrations/1721356124871_TeamMember-phase2.ts new file mode 100644 index 00000000000..f88c3c6a97d --- /dev/null +++ b/packages/server/postgres/migrations/1721356124871_TeamMember-phase2.ts @@ -0,0 +1,144 @@ +import {Kysely, PostgresDialect} from 'kysely' +import {r} from 'rethinkdb-ts' +import connectRethinkDB from '../../database/connectRethinkDB' +import getPg from '../getPg' + +export async function up() { + await connectRethinkDB() + const pg = new Kysely({ + dialect: new PostgresDialect({ + pool: getPg() + }) + }) + + // add a dummy date for nulls + const parabolEpoch = new Date('2016-06-01') + await r + .table('TeamMember') + .update((row) => ({ + updatedAt: row('updatedAt').default(parabolEpoch), + createdAt: row('createdAt').default(parabolEpoch) + })) + .run() + const strDates = await r + .table('TeamMember') + .filter((row) => row('updatedAt').typeOf().eq('STRING')) + .pluck('updatedAt', 'id', 'createdAt') + .run() + const dateDates = strDates.map((d) => ({ + id: d.id, + updatedAt: new Date(d.updatedAt), + createdAt: new Date(d.createdAt) + })) + // some dates are + await r(dateDates) + .forEach((row: any) => { + return r + .table('TeamMember') + .get(row('id')) + .update({updatedAt: row('updatedAt')}) + }) + .run() + + try { + console.log('Adding index') + await r + .table('TeamMember') + .indexCreate('updatedAtId', (row: any) => [row('updatedAt'), row('id')]) + .run() + await r.table('TeamMember').indexWait().run() + } catch { + // index already exists + } + + await console.log('Adding index complete') + const MAX_PG_PARAMS = 65545 + const PG_COLS = [ + 'id', + 'isNotRemoved', + 'isLead', + 'isSpectatingPoker', + 'email', + 'openDrawer', + 'picture', + 'preferredName', + 'teamId', + 'userId', + 'createdAt', + 'updatedAt' + ] as const + type TeamMember = { + [K in (typeof PG_COLS)[number]]: any + } + const BATCH_SIZE = Math.trunc(MAX_PG_PARAMS / PG_COLS.length) + + let curUpdatedAt = r.minval + let curId = r.minval + for (let i = 0; i < 1e6; i++) { + console.log('inserting row', i * BATCH_SIZE, String(curUpdatedAt), String(curId)) + const rawRowsToInsert = (await r + .table('TeamMember') + .between([curUpdatedAt, curId], [r.maxval, r.maxval], { + index: 'updatedAtId', + leftBound: 'open', + rightBound: 'closed' + }) + .orderBy({index: 'updatedAtId'}) + .limit(BATCH_SIZE) + .pluck(...PG_COLS) + .run()) as TeamMember[] + + const rowsToInsert = rawRowsToInsert.map((row) => { + const {preferredName, picture, ...rest} = row as any + return { + ...rest, + preferredName: preferredName.slice(0, 100), + picture: picture.slice(0, 2056) + } + }) + if (rowsToInsert.length === 0) break + const lastRow = rowsToInsert[rowsToInsert.length - 1] + curUpdatedAt = lastRow.updatedAt + curId = lastRow.id + try { + await pg + .insertInto('TeamMember') + .values(rowsToInsert) + .onConflict((oc) => oc.doNothing()) + .execute() + } catch (e) { + await Promise.all( + rowsToInsert.map(async (row) => { + try { + await pg + .insertInto('TeamMember') + .values(row) + .onConflict((oc) => oc.doNothing()) + .execute() + } catch (e) { + if (e.constraint === 'fk_userId' || e.constraint === 'fk_teamId') { + console.log(`Skipping ${row.id} because it has no user/team`) + return + } + console.log(e, row) + } + }) + ) + } + } +} + +export async function down() { + await connectRethinkDB() + try { + await r.table('TeamMember').indexDrop('updatedAtId').run() + } catch { + // index already dropped + } + const pg = new Kysely({ + dialect: new PostgresDialect({ + pool: getPg() + }) + }) + await pg.deleteFrom('TeamMember').execute() +} diff --git a/packages/server/postgres/utils/checkEqBase.ts b/packages/server/postgres/utils/checkEqBase.ts index bbe31117b3f..175d20a57a5 100644 --- a/packages/server/postgres/utils/checkEqBase.ts +++ b/packages/server/postgres/utils/checkEqBase.ts @@ -33,7 +33,7 @@ export const checkRowCount = async (tableName: string) => { } export async function checkTableEq( - rethinkQuery: (joinedAt: Date, id: string | number) => RSelection, + rethinkQuery: (updatedAt: Date, id: string | number) => RSelection, pgQuery: (ids: string[]) => Promise, equalityMap: Record boolean>, maxErrors: number | null | undefined @@ -51,7 +51,7 @@ export async function checkTableEq( .run()) as RethinkDoc[] if (rethinkRows.length === 0) break const lastRow = rethinkRows[rethinkRows.length - 1]! - curUpdatedDate = lastRow.joinedAt + curUpdatedDate = lastRow.updatedAt curId = lastRow.id const ids = rethinkRows.map((t) => t.id) const pgRows = (await pgQuery(ids)) ?? [] diff --git a/packages/server/postgres/utils/rethinkEqualityFns.ts b/packages/server/postgres/utils/rethinkEqualityFns.ts index 3d63e8381df..201afa28e9c 100644 --- a/packages/server/postgres/utils/rethinkEqualityFns.ts +++ b/packages/server/postgres/utils/rethinkEqualityFns.ts @@ -22,6 +22,11 @@ export const compareRealNumber = (rVal: unknown, pgVal: unknown) => { return answer } +export const compareRValUndefinedAs = + (as: string | number | boolean | null | undefined) => (rVal: unknown, pgVal: unknown) => { + const normalizedRVal = rVal === undefined ? as : rVal + return defaultEqFn(normalizedRVal, pgVal) + } export const compareRValUndefinedAsNull = (rVal: unknown, pgVal: unknown) => { const normalizedRVal = rVal === undefined ? null : rVal return defaultEqFn(normalizedRVal, pgVal) diff --git a/packages/server/utils/__tests__/isRequestToJoinDomainAllowed.test.ts b/packages/server/utils/__tests__/isRequestToJoinDomainAllowed.test.ts index 52b67b82600..dbe8589d6d8 100644 --- a/packages/server/utils/__tests__/isRequestToJoinDomainAllowed.test.ts +++ b/packages/server/utils/__tests__/isRequestToJoinDomainAllowed.test.ts @@ -72,6 +72,7 @@ afterEach(async () => { afterAll(async () => { await getKysely().destroy() getRedis().quit() + console.log('request to join destroy') }) test('Only the biggest org with verified emails qualify', async () => { From dbbf588069f49d870349c77dc59a5d79202964ef Mon Sep 17 00:00:00 2001 From: "parabol-release-bot[bot]" <150284312+parabol-release-bot[bot]@users.noreply.github.com> Date: Tue, 23 Jul 2024 16:32:47 -0700 Subject: [PATCH 03/18] chore(release): release v7.39.1 (#10020) Co-authored-by: parabol-release-bot[bot] <150284312+parabol-release-bot[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ package.json | 2 +- packages/chronos/package.json | 4 ++-- packages/client/package.json | 2 +- packages/embedder/package.json | 2 +- packages/gql-executor/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/server/package.json | 4 ++-- 9 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6054f981be4..af120f4b997 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.39.0" + ".": "7.39.1" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7739ff7b5..2d2ff9b9d19 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ This project adheres to [Semantic Versioning](http://semver.org/). This CHANGELOG follows conventions [outlined here](http://keepachangelog.com/). +## [7.39.1](https://github.com/ParabolInc/parabol/compare/v7.39.0...v7.39.1) (2024-07-23) + + +### Changed + +* **rethinkdb:** TeamMember: Phase 2 ([#9993](https://github.com/ParabolInc/parabol/issues/9993)) ([90de32f](https://github.com/ParabolInc/parabol/commit/90de32f702849a92be28dff42188445947a54325)) + ## [7.39.0](https://github.com/ParabolInc/parabol/compare/v7.38.11...v7.39.0) (2024-07-23) diff --git a/package.json b/package.json index 7c1052a333a..72f16a26c6c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.0", + "version": "7.39.1", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/chronos/package.json b/packages/chronos/package.json index c71da5ef114..07db583627b 100644 --- a/packages/chronos/package.json +++ b/packages/chronos/package.json @@ -1,6 +1,6 @@ { "name": "chronos", - "version": "7.39.0", + "version": "7.39.1", "description": "A cron job scheduler", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/chronos#readme", @@ -25,6 +25,6 @@ }, "dependencies": { "cron": "^2.3.1", - "parabol-server": "7.39.0" + "parabol-server": "7.39.1" } } diff --git a/packages/client/package.json b/packages/client/package.json index 1a7a040a775..f73ad247867 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.0", + "version": "7.39.1", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/embedder/package.json b/packages/embedder/package.json index 812dc94a797..b09be582a0e 100644 --- a/packages/embedder/package.json +++ b/packages/embedder/package.json @@ -1,6 +1,6 @@ { "name": "parabol-embedder", - "version": "7.39.0", + "version": "7.39.1", "description": "A service that computes embedding vectors from Parabol objects", "author": "Jordan Husney ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/embedder#readme", diff --git a/packages/gql-executor/package.json b/packages/gql-executor/package.json index 71a1e1dea06..8794d4de07f 100644 --- a/packages/gql-executor/package.json +++ b/packages/gql-executor/package.json @@ -1,6 +1,6 @@ { "name": "gql-executor", - "version": "7.39.0", + "version": "7.39.1", "description": "A Stateless GraphQL Executor", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/gqlExecutor#readme", @@ -27,8 +27,8 @@ }, "dependencies": { "dd-trace": "^4.2.0", - "parabol-client": "7.39.0", - "parabol-server": "7.39.0", + "parabol-client": "7.39.1", + "parabol-server": "7.39.1", "undici": "^5.26.2" } } diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index a0cb0594b08..ef1d9c5acc4 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -2,7 +2,7 @@ "name": "integration-tests", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.0", + "version": "7.39.1", "description": "", "main": "index.js", "scripts": { diff --git a/packages/server/package.json b/packages/server/package.json index ec51fbe3381..34ca2fd235b 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.0", + "version": "7.39.1", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" @@ -124,7 +124,7 @@ "openai": "^4.24.1", "openapi-fetch": "^0.9.7", "oy-vey": "^0.12.1", - "parabol-client": "7.39.0", + "parabol-client": "7.39.1", "pg": "^8.5.1", "react": "^17.0.2", "react-dom": "^17.0.2", From 0bb8ead2adc52f64ad30ef57891791e1b3dd4ac1 Mon Sep 17 00:00:00 2001 From: Matt Krick Date: Wed, 24 Jul 2024 10:49:21 -0700 Subject: [PATCH 04/18] fix: bump pm2 version (#10027) Signed-off-by: Matt Krick --- package.json | 2 +- yarn.lock | 573 +++++++++++++++++---------------------------------- 2 files changed, 193 insertions(+), 382 deletions(-) diff --git a/package.json b/package.json index 72f16a26c6c..3e6a45467c2 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "minimist": "^1.2.5", "node-loader": "^2.0.0", "pg-promise": "^11.2.0", - "pm2": "^5.3.1", + "pm2": "^5.4.2", "postcss": "^8.4.21", "postcss-loader": "^7.0.2", "prettier": "^3.2.5", diff --git a/yarn.lock b/yarn.lock index 774e599c991..0e96a64ad3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5451,36 +5451,6 @@ dependencies: "@octokit/openapi-types" "^13.10.0" -"@opencensus/core@0.0.9": - version "0.0.9" - resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.9.tgz#b16f775435ee309433e4126af194d37313fc93b3" - integrity sha512-31Q4VWtbzXpVUd2m9JS6HEaPjlKvNMOiF7lWKNmXF84yUcgfAFL5re7/hjDmdyQbOp32oGc+RFV78jXIldVz6Q== - dependencies: - continuation-local-storage "^3.2.1" - log-driver "^1.2.7" - semver "^5.5.0" - shimmer "^1.2.0" - uuid "^3.2.1" - -"@opencensus/core@^0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.8.tgz#df01f200c2d2fbfe14dae129a1a86fb87286db92" - integrity sha512-yUFT59SFhGMYQgX0PhoTR0LBff2BEhPrD9io1jWfF/VDbakRfs6Pq60rjv0Z7iaTav5gQlttJCX2+VPxFWCuoQ== - dependencies: - continuation-local-storage "^3.2.1" - log-driver "^1.2.7" - semver "^5.5.0" - shimmer "^1.2.0" - uuid "^3.2.1" - -"@opencensus/propagation-b3@0.0.8": - version "0.0.8" - resolved "https://registry.yarnpkg.com/@opencensus/propagation-b3/-/propagation-b3-0.0.8.tgz#0751e6fd75f09400d9d3c419001e9e15a0df68e9" - integrity sha512-PffXX2AL8Sh0VHQ52jJC4u3T0H6wDK6N/4bg7xh4ngMYOIi13aR1kzVvX1sVDBgfGwDOkMbl4c54Xm3tlPx/+A== - dependencies: - "@opencensus/core" "^0.0.8" - uuid "^3.2.1" - "@opentelemetry/api@^1.0.0": version "1.4.1" resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.4.1.tgz#ff22eb2e5d476fbc2450a196e40dd243cc20c28f" @@ -5635,9 +5605,9 @@ fsevents "2.3.2" "@pm2/agent@~2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@pm2/agent/-/agent-2.0.1.tgz#0edffc54cd8ee2b12f90136264e7880f3f78c79d" - integrity sha512-QKHMm6yexcvdDfcNE7PL9D6uEjoQPGRi+8dh+rc4Hwtbpsbh5IAvZbz3BVGjcd4HaX6pt2xGpOohG7/Y2L4QLw== + version "2.0.4" + resolved "https://registry.yarnpkg.com/@pm2/agent/-/agent-2.0.4.tgz#a6699a6c57741492129776eb5a7abc15e50672cb" + integrity sha512-n7WYvvTJhHLS2oBb1PjOtgLpMhgImOq8sXkPBw6smeg9LJBWZjiEgPKOpR8mn9UJZsB5P3W4V/MyvNnp31LKeA== dependencies: async "~3.2.0" chalk "~3.0.0" @@ -5649,22 +5619,20 @@ nssocket "0.6.0" pm2-axon "~4.0.1" pm2-axon-rpc "~0.7.0" - proxy-agent "~5.0.0" - semver "~7.2.0" - ws "~7.4.0" + proxy-agent "~6.3.0" + semver "~7.5.0" + ws "~7.5.10" -"@pm2/io@~5.0.0": - version "5.0.0" - resolved "https://registry.yarnpkg.com/@pm2/io/-/io-5.0.0.tgz#623cbcaf6fe39375f20ac2e75497477a1b1ec5c5" - integrity sha512-3rToDVJaRoob5Lq8+7Q2TZFruoEkdORxwzFpZaqF4bmH6Bkd7kAbdPrI/z8X6k1Meq5rTtScM7MmDgppH6aLlw== +"@pm2/io@~6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/@pm2/io/-/io-6.0.1.tgz#300fefa485317f26e3bc348da061ca395a52149a" + integrity sha512-KiA+shC6sULQAr9mGZ1pg+6KVW9MF8NpG99x26Lf/082/Qy8qsTCtnJy+HQReW1A9Rdf0C/404cz0RZGZro+IA== dependencies: - "@opencensus/core" "0.0.9" - "@opencensus/propagation-b3" "0.0.8" async "~2.6.1" debug "~4.3.1" eventemitter2 "^6.3.1" require-in-the-middle "^5.0.0" - semver "6.3.0" + semver "~7.5.4" shimmer "^1.2.0" signal-exit "^3.0.3" tslib "1.9.3" @@ -8810,7 +8778,7 @@ acorn-walk@^7.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -acorn-walk@^8.0.0, acorn-walk@^8.0.2, acorn-walk@^8.2.0: +acorn-walk@^8.0.0, acorn-walk@^8.0.2: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== @@ -8825,7 +8793,7 @@ acorn@^7.0.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.0.4, acorn@^8.1.0, acorn@^8.7.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.1.0, acorn@^8.7.1, acorn@^8.8.0, acorn@^8.8.2, acorn@^8.9.0: version "8.10.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5" integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== @@ -8850,7 +8818,7 @@ addressparser@1.0.1: resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" integrity sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y= -agent-base@6, agent-base@^6.0.0, agent-base@^6.0.2: +agent-base@6, agent-base@^6.0.2: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== @@ -8935,14 +8903,14 @@ ajv@^8.0.0, ajv@^8.10.0, ajv@^8.12.0, ajv@^8.6.0, ajv@^8.8.0, ajv@^8.9.0: amp-message@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/amp-message/-/amp-message-0.1.2.tgz#a78f1c98995087ad36192a41298e4db49e3dfc45" - integrity sha1-p48cmJlQh602GSpBKY5NtJ49/EU= + integrity sha512-JqutcFwoU1+jhv7ArgW38bqrE+LQdcRv4NxNw0mp0JHQyB6tXesWRjtYKlDgHRY2o3JE5UTaBGUK8kSWUdxWUg== dependencies: amp "0.3.1" amp@0.3.1, amp@~0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/amp/-/amp-0.3.1.tgz#6adf8d58a74f361e82c1fa8d389c079e139fc47d" - integrity sha1-at+NWKdPNh6CwfqNOJwHnhOfxH0= + integrity sha512-OwIuC4yZaRogHKiuU5WlMR5Xk/jAcpPtawWL05Gj8Lvm2F6mwoJt4O/bHI+DHwG79vWd+8OFYM4/BzYqyRd3qw== analytics-node@^6.0.0: version "6.2.0" @@ -8959,9 +8927,9 @@ analytics-node@^6.0.0: uuid "^8.3.2" ansi-colors@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: version "4.3.2" @@ -9014,7 +8982,7 @@ any-promise@^1.0.0: resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" integrity sha1-q8av7tzqUugJzcA3au0845Y10X8= -anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: +anymatch@^3.0.0, anymatch@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -9022,6 +8990,14 @@ anymatch@^3.0.0, anymatch@^3.0.3, anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + api@^5.0.7: version "5.0.8" resolved "https://registry.yarnpkg.com/api/-/api-5.0.8.tgz#a58e70f9efaf68318206521cae5f659e87035f4c" @@ -9228,7 +9204,7 @@ ast-types@0.15.2: dependencies: tslib "^2.0.1" -ast-types@^0.13.2, ast-types@^0.13.4: +ast-types@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.13.4.tgz#ee0d77b343263965ecc3fb62da16e7222b2b6782" integrity sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w== @@ -9240,14 +9216,6 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-listener@^0.6.0: - version "0.6.10" - resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" - integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw== - dependencies: - semver "^5.3.0" - shimmer "^1.1.0" - async-retry@1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.1.tgz#139f31f8ddce50c0870b0ba558a6079684aaed55" @@ -9262,7 +9230,12 @@ async@^2.6.3, async@~2.6.1: dependencies: lodash "^4.17.14" -async@^3.2.0, async@^3.2.3, async@~3.2.0: +async@^3.2.0, async@~3.2.0: + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +async@^3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9" integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g== @@ -9611,9 +9584,9 @@ bin-links@^3.0.0: write-file-atomic "^4.0.0" binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522" + integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== bl@^4.0.3, bl@^4.1.0: version "4.1.0" @@ -9627,12 +9600,12 @@ bl@^4.0.3, bl@^4.1.0: blessed@0.1.81: version "0.1.81" resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" - integrity sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk= + integrity sha512-LoF5gae+hlmfORcG1M5+5XZi4LBmvlXTzwJWzUlPryN/SJdSflZvROM2TwkT0GMpq7oqT48NRd4GS7BiVBc5OQ== bodec@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/bodec/-/bodec-0.1.0.tgz#bc851555430f23c9f7650a75ef64c6a94c3418cc" - integrity sha1-vIUVVUMPI8n3ZQp172TGqUw0GMw= + integrity sha512-Ylo+MAo5BDUq1KA3f3R/MFhh+g8cnHmo8bz3YPGhI1znrMaf77ol1sfvYJzsw3nTE+Y2GryfDxBaR+AqpAkEHQ== body-parser@1.20.2: version "1.20.2" @@ -9685,13 +9658,20 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@^3.0.2, braces@~3.0.2: +braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: fill-range "^7.0.1" +braces@~3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== + dependencies: + fill-range "^7.1.1" + browserslist@^4.14.5, browserslist@^4.21.1, browserslist@^4.21.4, browserslist@^4.21.9: version "4.21.10" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" @@ -10058,7 +10038,7 @@ charenc@0.0.2: charm@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/charm/-/charm-0.1.2.tgz#06c21eed1a1b06aeb67553cdc53e23274bac2296" - integrity sha1-BsIe7RobBq62dVPNxT4jJ0usIpY= + integrity sha512-syedaZ9cPe7r3hoQA9twWYKu5AIyCswN5+szkmPBe9ccdLrj4bYaCnLVPTLd2kgVRc7+zoX4tyPgRnFKCj5YjQ== chart.js@^3.8.0: version "3.8.0" @@ -10114,22 +10094,7 @@ chokidar@^3.3.1, chokidar@^3.4.0, chokidar@^3.5.1: optionalDependencies: fsevents "~2.3.2" -chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chokidar@^3.6.0: +chokidar@^3.5.3, chokidar@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -10531,7 +10496,7 @@ compute-lcm@^1.1.2: concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== concat-stream@^2.0.0: version "2.0.0" @@ -10602,14 +10567,6 @@ content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -continuation-local-storage@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" - integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA== - dependencies: - async-listener "^0.6.0" - emitter-listener "^1.1.1" - conventional-changelog-angular@^5.0.12: version "5.0.13" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz#896885d63b914a70d4934b59d2fe7bde1832b28c" @@ -10954,7 +10911,7 @@ csstype@^3.0.2, csstype@^3.1.0: culvert@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/culvert/-/culvert-0.1.2.tgz#9502f5f0154a2d5a22a023e79f71cc936fa6ef6f" - integrity sha1-lQL18BVKLVoioCPnn3HMk2+m728= + integrity sha512-yi1x3EAWKjQTreYWeSd98431AV+IEE0qoDyOoaHJ7KJ21gv6HtBXHVLX74opVSGqcR8/AbjJBHAHpcOy2bj5Gg== d@1, d@^1.0.1: version "1.0.1" @@ -10981,11 +10938,6 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" -data-uri-to-buffer@3: - version "3.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz#594b8973938c5bc2c33046535785341abc4f3636" - integrity sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og== - data-uri-to-buffer@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-6.0.1.tgz#540bd4c8753a25ee129035aebdedf63b078703c7" @@ -11036,11 +10988,16 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -dayjs@^1.11.3, dayjs@~1.11.5: +dayjs@^1.11.3: version "1.11.9" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== +dayjs@~1.11.5: + version "1.11.12" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d" + integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg== + dayjs@~1.8.24: version "1.8.36" resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.8.36.tgz#be36e248467afabf8f5a86bae0de0cdceecced50" @@ -11100,10 +11057,10 @@ debug@3.1.0: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== +debug@4, debug@^4.1.1, debug@^4.3.1, debug@~4.3.1: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: ms "2.1.2" @@ -11114,6 +11071,13 @@ debug@^3.2.6: dependencies: ms "^2.1.1" +debug@^4.1.0, debug@^4.2.0, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -11166,7 +11130,7 @@ deep-extend@0.6.0, deep-extend@^0.6.0: resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== -deep-is@^0.1.3, deep-is@~0.1.3: +deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -11230,16 +11194,6 @@ defined@^1.0.0: resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.1.tgz#c0b9db27bfaffd95d6f61399419b893df0f91ebf" integrity sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q== -degenerator@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-3.0.1.tgz#7ef78ec0c8577a544477308ddf1d2d6e88d51f5b" - integrity sha512-LFsIFEeLPlKvAKXu7j3ssIG6RT0TbI7/GhsqrI0DnHASEQjXQ0LUSYcjJteGgRGmZbl1TnMSxpNQIAiJ7Du5TQ== - dependencies: - ast-types "^0.13.2" - escodegen "^1.8.1" - esprima "^4.0.0" - vm2 "^3.9.3" - degenerator@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/degenerator/-/degenerator-5.0.1.tgz#9403bf297c6dad9a1ece409b37db27954f91f2f5" @@ -11665,13 +11619,6 @@ email-addresses@^3.0.1: resolved "https://registry.yarnpkg.com/email-addresses/-/email-addresses-3.1.0.tgz#cabf7e085cbdb63008a70319a74e6136188812fb" integrity sha512-k0/r7GrWVL32kZlGwfPNgB2Y/mMXVTq/decgLczm/j34whdaspNrZO8CnXPf1laaHxI6ptUlsnAxN+UAPw+fzg== -emitter-listener@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" - integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== - dependencies: - shimmer "^1.2.0" - emittery@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" @@ -11895,18 +11842,6 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escodegen@^1.8.1: - version "1.14.3" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" - integrity sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw== - dependencies: - esprima "^4.0.1" - estraverse "^4.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - escodegen@^2.0.0, escodegen@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.1.0.tgz#ba93bbb7a43986d29d6041f99f5262da773e2e17" @@ -12063,7 +11998,7 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" -estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -12109,17 +12044,17 @@ eventemitter-asyncresource@^1.0.0: eventemitter2@5.0.1, eventemitter2@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452" - integrity sha1-YZegldX7a1folC9v1+qtY6CclFI= + integrity sha512-5EM1GHXycJBS6mauYAbVKT1cVs7POKWb2NXD4Vyt8dDqeZa7LaDK1/sjtL+Zb0lzTpSNil4596Dyu97hz37QLg== eventemitter2@^6.3.1: - version "6.4.5" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.5.tgz#97380f758ae24ac15df8353e0cc27f8b95644655" - integrity sha512-bXE7Dyc1i6oQElDG0jMRZJrRAn9QR2xyyFGmBdZleNmyQX0FqGYmhZIrIrpPfm/w//LTo4tVQGOGQcGCb5q9uw== + version "6.4.9" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-6.4.9.tgz#41f2750781b4230ed58827bc119d293471ecb125" + integrity sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg== eventemitter2@~0.4.14: version "0.4.14" resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" - integrity sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas= + integrity sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ== eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" @@ -12340,7 +12275,7 @@ fast-json-stringify@^5.7.0: json-schema-ref-resolver "^1.0.1" rfdc "^1.2.0" -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -12435,7 +12370,7 @@ fbjs@^0.8.15, fbjs@^3.0.0, fbjs@^3.0.2, fbjs@^3.0.4: fclone@1.0.11, fclone@~1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" - integrity sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA= + integrity sha512-GDqVQezKzRABdeqflsgMr7ktzgF9CyS+p2oe0jJqUY6izSSbhPIQJDpoU4PtGcD7VPM9xh/dVrTu6z1nwgmEGw== fetch-har@^8.1.5: version "8.1.5" @@ -12470,11 +12405,6 @@ file-loader@6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" -file-uri-to-path@2: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" - integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== - filelist@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.3.tgz#448607750376484932f67ef1b9ff07386b036c83" @@ -12482,10 +12412,10 @@ filelist@^1.0.1: dependencies: minimatch "^5.0.1" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.0.1, fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" @@ -12744,27 +12674,19 @@ fs-readdir-recursive@^1.1.0: fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== -fsevents@2.3.2, fsevents@^2.3.2, fsevents@~2.3.2: +fsevents@2.3.2, fsevents@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -ftp@^0.3.10: - version "0.3.10" - resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" - integrity sha1-kZfYYa2BQvPmPVqDv+TFn3MwiF0= - dependencies: - readable-stream "1.1.x" - xregexp "2.0.0" - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.2: +function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== @@ -12920,18 +12842,6 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" -get-uri@3: - version "3.0.2" - resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-3.0.2.tgz#f0ef1356faabc70e1f9404fa3b66b2ba9bfc725c" - integrity sha512-+5s0SJbGoyiJTZZ2JTpFPLMPSch72KEqGOTvQsBqg0RBWvwhWUSYZFAtz3TPW0GXJuLBJPts1E241iHg+VRfhg== - dependencies: - "@tootallnate/once" "1" - data-uri-to-buffer "3" - debug "4" - file-uri-to-path "2" - fs-extra "^8.1.0" - ftp "^0.3.10" - get-uri@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/get-uri/-/get-uri-6.0.2.tgz#e019521646f4a8ff6d291fbaea2c46da204bb75b" @@ -12963,7 +12873,7 @@ git-diff@^2.0.6: git-node-fs@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/git-node-fs/-/git-node-fs-1.0.0.tgz#49b215e242ebe43aa4c7561bbba499521752080f" - integrity sha1-SbIV4kLr5Dqkx1Ybu6SZUhdSCA8= + integrity sha512-bLQypt14llVXBg0S0u8q8HmU7g9p3ysH+NvVlae5vILuUvs759665HvmR5+wb04KjHyjFcDRxdYb4kyNnluMUQ== git-raw-commits@^2.0.8: version "2.0.11" @@ -12995,7 +12905,7 @@ git-semver-tags@^4.1.1: git-sha1@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/git-sha1/-/git-sha1-0.1.2.tgz#599ac192b71875825e13a445f3a6e05118c2f745" - integrity sha1-WZrBkrcYdYJeE6RF86bgURjC90U= + integrity sha512-2e/nZezdVlyCopOCYHeW0onkbZg7xP1Ad6pndPy1rCygeRykefUS6r7oA5cJRGEFvseiaz5a/qUHFVX1dd6Isg== git-up@^6.0.0: version "6.0.0" @@ -13082,7 +12992,7 @@ glob@^10.3.7: minipass "^7.0.4" path-scurry "^1.10.2" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -13438,11 +13348,9 @@ has-unicode@^2.0.0, has-unicode@^2.0.1: integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" + version "1.0.4" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6" + integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ== hasown@^2.0.0: version "2.0.0" @@ -13451,6 +13359,13 @@ hasown@^2.0.0: dependencies: function-bind "^1.1.2" +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hdr-histogram-js@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/hdr-histogram-js/-/hdr-histogram-js-2.0.3.tgz#0b860534655722b6e3f3e7dca7b78867cf43dcb5" @@ -13643,7 +13558,7 @@ http-parser-js@>=0.5.1: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.5.tgz#d7c30d5d3c90d865b4a2e870181f9d6f22ac7ac5" integrity sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA== -http-proxy-agent@^4.0.0, http-proxy-agent@^4.0.1: +http-proxy-agent@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== @@ -13702,7 +13617,7 @@ http2-client@^1.2.5: resolved "https://registry.yarnpkg.com/http2-client/-/http2-client-1.3.5.tgz#20c9dc909e3cc98284dd20af2432c524086df181" integrity sha512-EC2utToWl4RKfs5zd36Mxq7nzHHBuomZboI0yYL6Y0RmBgT7Sgkq4rQ0ezFTYoIsSs7Tm9SJe+o2FcAg6GBhGA== -https-proxy-agent@5, https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: +https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== @@ -13895,12 +13810,12 @@ infer-owner@^1.0.4: inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -14018,7 +13933,7 @@ ioredis@^5.2.3: redis-parser "^3.0.0" standard-as-callback "^2.1.0" -ip@^1.1.5, ip@^1.1.8: +ip@^1.1.8: version "1.1.9" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.9.tgz#8dfbcc99a754d07f425310b86a99546b1151e396" integrity sha512-cyRxvOEpNHNtchU3Ln9KC/auJgup87llfQpQ+t5ghoC/UhL16SWzbueiCsdTnWmqAWl7LadfuwhlqmtOaqMHdQ== @@ -14107,13 +14022,20 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.0" -is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.1, is-core-module@^2.9.0: +is-core-module@^2.2.0, is-core-module@^2.5.0, is-core-module@^2.8.1: version "2.11.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.11.0.tgz#ad4cb3e3863e814523c96f3f58d26cc570ff0144" integrity sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw== dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.15.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.15.0.tgz#71c72ec5442ace7e76b306e9d48db361f22699ea" + integrity sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA== + dependencies: + hasown "^2.0.2" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -14146,7 +14068,7 @@ is-extendable@^1.0.0: is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-fullwidth-code-point@^1.0.0: version "1.0.0" @@ -14425,7 +14347,7 @@ is-wsl@^3.1.0: isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= + integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ== isarray@~1.0.0: version "1.0.0" @@ -14945,7 +14867,7 @@ jose@^4.11.4: js-git@^0.7.8: version "0.7.8" resolved "https://registry.yarnpkg.com/js-git/-/js-git-0.7.8.tgz#52fa655ab61877d6f1079efc6534b554f31e5444" - integrity sha1-UvplWrYYd9bxB578ZTS1VPMeVEQ= + integrity sha512-+E5ZH/HeRnoc/LW0AmAyhU+mNcWBzAKE+30+IDMLSLbbK+Tdt02AdkOKq9u15rlJsDEGFqtgckc8ZM59LhhiUA== dependencies: bodec "^0.1.0" culvert "^0.1.2" @@ -14965,7 +14887,7 @@ js-yaml@3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0: +js-yaml@4.1.0, js-yaml@^4.0.0, js-yaml@^4.1.0, js-yaml@~4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== @@ -15141,7 +15063,7 @@ json-stringify-nice@^1.1.4: json-stringify-safe@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== json-to-ast@^2.0.3: version "2.1.0" @@ -15181,7 +15103,7 @@ jsonc-parser@3.2.0: jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== optionalDependencies: graceful-fs "^4.1.6" @@ -15344,7 +15266,7 @@ launch-editor@^2.6.1: lazy@~1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690" - integrity sha1-2qBoIGKCVCwIgojpdcKXwa53tpA= + integrity sha512-Y+CjUfLmIpoUCCRl0ub4smrYtGGr5AOa2AKOaWelGHOGz33X/Y/KizefGqbkwfz44+cnq/+9habclf8vOmu2LA== lerna@^6.4.1: version "6.4.1" @@ -15390,14 +15312,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - libbase64@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-0.1.0.tgz#62351a839563ac5ff5bd26f12f60e9830bb751e6" @@ -15751,11 +15665,6 @@ lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17 resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-driver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" - integrity sha512-U7KCmLdqsGHBLeWqYlFA0V0Sl6P08EE1ZrmA9cxjUE0WVqT9qnyVDPz1kzpFEP0jdJuFnasWIfSd7fsaNXkpbg== - log-symbols@^4.0.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -16358,7 +16267,7 @@ modify-values@^1.0.0: module-details-from-path@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/module-details-from-path/-/module-details-from-path-1.0.3.tgz#114c949673e2a8a35e9d35788527aa37b679da2b" - integrity sha1-EUyUlnPiqKNenTV4hSeqN7Z52is= + integrity sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A== moment@^2.29.3: version "2.29.4" @@ -16510,7 +16419,7 @@ nest-graphql-endpoint@mattkrick/nest-graphql-endpoint#add-options: tslib "~2.3.1" typescript "^4.2.4" -netmask@^2.0.1, netmask@^2.0.2: +netmask@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/netmask/-/netmask-2.0.2.tgz#8b01a07644065d536383835823bc52004ebac5e7" integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg== @@ -16892,7 +16801,7 @@ npmlog@^6.0.0, npmlog@^6.0.2: nssocket@0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/nssocket/-/nssocket-0.6.0.tgz#59f96f6ff321566f33c70f7dbeeecdfdc07154fa" - integrity sha1-Wflvb/MhVm8zxw99vu7N/cBxVPo= + integrity sha512-a9GSOIql5IqgWJR3F/JXG4KpJTA3Z53Cj0MeMvGpglytB1nxE4PdFNC0jINe27CS7cGivoynwc054EzCcT3M3w== dependencies: eventemitter2 "~0.4.14" lazy "~1.0.11" @@ -17152,7 +17061,7 @@ on-headers@~1.0.2: once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" @@ -17236,18 +17145,6 @@ opentracing@>=0.12.1: resolved "https://registry.yarnpkg.com/opentracing/-/opentracing-0.14.5.tgz#891fa92cd90a24e64f99bc964370227310926c85" integrity sha512-XLKtEfHxqrWyF1fzxznsv78w3csW41ucHnjiKnfzZLD5FN8UBDZZL1i4q0FR29zjxXhm+2Hop+5Vr/b8tKIvEg== -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - optionator@^0.9.3: version "0.9.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.3.tgz#007397d44ed1872fdc6ed31360190f81814e2c64" @@ -17429,21 +17326,6 @@ p-waterfall@^2.1.1: dependencies: p-reduce "^2.0.0" -pac-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-5.0.0.tgz#b718f76475a6a5415c2efbe256c1c971c84f635e" - integrity sha512-CcFG3ZtnxO8McDigozwE3AqAw15zDvGH+OjXO4kzf7IkEKkQ4gxQ+3sdF50WmhQ4P/bVusXcqNE2S3XrNURwzQ== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - get-uri "3" - http-proxy-agent "^4.0.1" - https-proxy-agent "5" - pac-resolver "^5.0.0" - raw-body "^2.2.0" - socks-proxy-agent "5" - pac-proxy-agent@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-7.0.1.tgz#6b9ddc002ec3ff0ba5fdf4a8a21d363bcc612d75" @@ -17458,15 +17340,6 @@ pac-proxy-agent@^7.0.1: pac-resolver "^7.0.0" socks-proxy-agent "^8.0.2" -pac-resolver@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-5.0.0.tgz#1d717a127b3d7a9407a16d6e1b012b13b9ba8dc0" - integrity sha512-H+/A6KitiHNNW+bxBKREk2MCGSxljfqRX76NjummWEYIat7ldVXRU3dhRIE3iXZ0nvGBk6smv3nntxKkzRL8NA== - dependencies: - degenerator "^3.0.1" - ip "^1.1.5" - netmask "^2.0.1" - pac-resolver@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-7.0.0.tgz#79376f1ca26baf245b96b34c339d79bff25e900c" @@ -17511,7 +17384,7 @@ pacote@^13.0.3, pacote@^13.6.1: pako@^0.2.5: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= + integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== pako@^1.0.10, pako@^1.0.3, pako@~1.0.2: version "1.0.11" @@ -17646,7 +17519,7 @@ path-extra@^1.0.2: path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-is-inside@^1.0.2: version "1.0.2" @@ -17809,9 +17682,9 @@ pidusage@^2.0.21: safe-buffer "^5.2.1" pidusage@~3.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-3.0.0.tgz#69108079724c9afdd958644b920bc40bac964044" - integrity sha512-8VJLToXhj+RYZGNVw8oxc7dS54iCQXUJ+MDFHezQ/fwF5B8W4OWodAMboc1wb08S/4LiHwAmkT4ohf/d3YPPsw== + version "3.0.2" + resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-3.0.2.tgz#6faa5402b2530b3af2cf93d13bcf202889724a53" + integrity sha512-g0VU+y08pKw5M8EZ2rIGiEBaB8wrQMjYGFfW2QVIfyT8V+fq8YFLkvlz4bz5ljvFDJYNFCWT3PWqcRr2FKO81w== dependencies: safe-buffer "^5.2.1" @@ -17917,7 +17790,7 @@ pm2-deploy@~1.0.2: pm2-multimeter@^0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz#1a1e55153d41a05534cea23cfe860abaa0eb4ace" - integrity sha1-Gh5VFT1BoFU0zqI8/oYKuqDrSs4= + integrity sha512-S+wT6XfyKfd7SJIBqRgOctGxaBzUOmVQzTAS+cg04TsEUObJVreha7lvCfX8zzGVr871XwCSnHUU7DQQ5xEsfA== dependencies: charm "~0.1.1" @@ -17932,13 +17805,13 @@ pm2-sysmonit@^1.2.8: systeminformation "^5.7" tx2 "~1.0.4" -pm2@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/pm2/-/pm2-5.3.1.tgz#f4c1b1199aac2988e9079ca4f127adaa1a5d18ce" - integrity sha512-DLVQHpSR1EegaTaRH3KbRXxpPVaqYwAp3uHSCtCsS++LSErvk07WSxuUnntFblBRqNU/w2KQyqs12mSq5wurkg== +pm2@^5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/pm2/-/pm2-5.4.2.tgz#34a50044cf772c5528d68e2713f84383ebb2e09b" + integrity sha512-ynVpBwZampRH3YWLwRepZpQ7X3MvpwLIaqIdFEeBYEhaXbHmEx2KqOdxGV4T54wvKBhH3LixvU1j1bK4/sq7Tw== dependencies: "@pm2/agent" "~2.0.0" - "@pm2/io" "~5.0.0" + "@pm2/io" "~6.0.1" "@pm2/js-api" "~0.8.0" "@pm2/pm2-version-check" latest async "~3.2.0" @@ -17953,6 +17826,7 @@ pm2@^5.3.1: enquirer "2.3.6" eventemitter2 "5.0.1" fclone "1.0.11" + js-yaml "~4.1.0" mkdirp "1.0.4" needle "2.4.0" pidusage "~3.0" @@ -17965,7 +17839,6 @@ pm2@^5.3.1: source-map-support "0.5.21" sprintf-js "1.1.2" vizion "~2.2.1" - yamljs "0.3.0" optionalDependencies: pm2-sysmonit "^1.2.8" @@ -18119,11 +17992,6 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - prettier-plugin-organize-imports@^3.2.4: version "3.2.4" resolved "https://registry.yarnpkg.com/prettier-plugin-organize-imports/-/prettier-plugin-organize-imports-3.2.4.tgz#77967f69d335e9c8e6e5d224074609309c62845e" @@ -18209,7 +18077,7 @@ promise@^7.1.1: promptly@^2: version "2.2.0" resolved "https://registry.yarnpkg.com/promptly/-/promptly-2.2.0.tgz#2a13fa063688a2a5983b161fff0108a07d26fc74" - integrity sha1-KhP6BjaIoqWYOxYf/wEIoH0m/HQ= + integrity sha512-aC9j+BZsRSSzEsXBNBwDnAxujdx19HycZoKgRgzWnS8eOHg1asuf9heuLprfbe739zY3IdUQx+Egv6Jn135WHA== dependencies: read "^1.0.4" @@ -18471,21 +18339,21 @@ proxy-agent@^6.4.0: proxy-from-env "^1.1.0" socks-proxy-agent "^8.0.2" -proxy-agent@~5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-5.0.0.tgz#d31405c10d6e8431fde96cba7a0c027ce01d633b" - integrity sha512-gkH7BkvLVkSfX9Dk27W6TyNOWWZWRilRfk1XxGNWOYJ2TuedAv1yFpCaU9QSBmBe716XOTNpYNOzhysyw8xn7g== +proxy-agent@~6.3.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-6.3.1.tgz#40e7b230552cf44fd23ffaf7c59024b692612687" + integrity sha512-Rb5RVBy1iyqOtNl15Cw/llpeLH8bsb37gM1FUfKQ+Wck6xHlbAhWGUFiTRHtkjqGTA5pSHz6+0hrPW/oECihPQ== dependencies: - agent-base "^6.0.0" - debug "4" - http-proxy-agent "^4.0.0" - https-proxy-agent "^5.0.0" - lru-cache "^5.1.1" - pac-proxy-agent "^5.0.0" - proxy-from-env "^1.0.0" - socks-proxy-agent "^5.0.0" + agent-base "^7.0.2" + debug "^4.3.4" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.2" + lru-cache "^7.14.1" + pac-proxy-agent "^7.0.1" + proxy-from-env "^1.1.0" + socks-proxy-agent "^8.0.2" -proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: +proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== @@ -18622,16 +18490,6 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -raw-body@^2.2.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-loader@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" @@ -18985,16 +18843,6 @@ read@1, read@^1.0.4, read@^1.0.7: dependencies: mute-stream "~0.0.4" -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" @@ -19270,13 +19118,13 @@ require-from-string@^2.0.2: integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== require-in-the-middle@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.1.0.tgz#b768f800377b47526d026bbf5a7f727f16eb412f" - integrity sha512-M2rLKVupQfJ5lf9OvqFGIT+9iVLnTmjgbOmpil12hiSQNn5zJTKGPoIisETNjfK+09vP3rpm1zJajmErpr2sEQ== + version "5.2.0" + resolved "https://registry.yarnpkg.com/require-in-the-middle/-/require-in-the-middle-5.2.0.tgz#4b71e3cc7f59977100af9beb76bf2d056a5a6de2" + integrity sha512-efCx3b+0Z69/LGJmm9Yvi4cqEdxnoGnxYxGxBghkkTTFeXRtTCmmhO0AnAfHz59k957uTSuy8WaHqOs8wbYUWg== dependencies: debug "^4.1.1" module-details-from-path "^1.0.3" - resolve "^1.12.0" + resolve "^1.22.1" require-main-filename@^2.0.0: version "2.0.0" @@ -19332,7 +19180,7 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.0.0, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.9.0: +resolve@^1.0.0, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.9.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== @@ -19341,7 +19189,7 @@ resolve@^1.0.0, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14. path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^1.1.6: +resolve@^1.1.6, resolve@^1.12.0: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -19552,11 +19400,16 @@ sanitizer@^0.1.3: resolved "https://registry.yarnpkg.com/sanitizer/-/sanitizer-0.1.3.tgz#d4f0af7475d9a7baf2a9e5a611718baa178a39e1" integrity sha1-1PCvdHXZp7ryqeWmEXGLqheKOeE= -sax@>=0.6.0, sax@^1.2.4: +sax@>=0.6.0: version "1.3.0" resolved "https://registry.yarnpkg.com/sax/-/sax-1.3.0.tgz#a5dbe77db3be05c9d1ee7785dbd3ea9de51593d0" integrity sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA== +sax@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + saxes@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" @@ -19624,16 +19477,11 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.6.0, semver@^5.7.1: version "5.7.2" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - semver@7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" @@ -19646,7 +19494,7 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" -semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: +semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4, semver@~7.5.0, semver@~7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -19658,10 +19506,10 @@ semver@^6.0.0, semver@^6.1.1, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@~7.2.0: - version "7.2.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.2.3.tgz#3641217233c6382173c76bf2c7ecd1e1c16b0d8a" - integrity sha512-utbW9Z7ZxVvwiIWkdOMLOR9G/NFXh2aRucghkVrEMJWuC++r3lCkBC3LwqBinyHzGMAJxY5tn6VakZGHObq5ig== +semver@^7.2: + version "7.6.3" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.3.tgz#980f7b5550bc175fb4dc09403085627f9eb33143" + integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== send@0.18.0: version "0.18.0" @@ -19820,7 +19668,7 @@ shelljs@^0.8.1: interpret "^1.0.0" rechoir "^0.6.2" -shimmer@^1.1.0, shimmer@^1.2.0: +shimmer@^1.2.0: version "1.2.1" resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== @@ -20000,15 +19848,6 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" -socks-proxy-agent@5, socks-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-5.0.1.tgz#032fb583048a29ebffec2e6a73fca0761f48177e" - integrity sha512-vZdmnjb9a2Tz6WEQVIurybSwElwPxMZaIc7PzqbJTrezcKNznv6giT7J7tZDZ1BojVaa1jvO/UiUdhDVB0ACoQ== - dependencies: - agent-base "^6.0.2" - debug "4" - socks "^2.3.3" - socks-proxy-agent@^6.0.0: version "6.2.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz#2687a31f9d7185e38d530bef1944fe1f1496d6ce" @@ -20036,7 +19875,7 @@ socks-proxy-agent@^8.0.2: debug "^4.3.4" socks "^2.7.1" -socks@^2.3.3, socks@^2.6.2, socks@^2.7.1: +socks@^2.6.2, socks@^2.7.1: version "2.7.1" resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== @@ -20204,7 +20043,7 @@ sprintf-js@1.1.2: sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== ssh2-streams@0.4.10: version "0.4.10" @@ -20430,11 +20269,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -20654,9 +20488,9 @@ symbol-tree@^3.2.4: integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== systeminformation@^5.7: - version "5.21.11" - resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.21.11.tgz#c1a0b2f0f338903bb7bc1759db9fcd34b7312360" - integrity sha512-dIJEGoP5W7k4JJGje/b+inJrOL5hV9LPsUi5ndBvJydI80CVEcu2DZYgt6prdRErDi2SA4SqYd/WMR4b+u34mA== + version "5.22.11" + resolved "https://registry.yarnpkg.com/systeminformation/-/systeminformation-5.22.11.tgz#42be7b650ce0a8b940c06219a6647f6ab3f7a349" + integrity sha512-aLws5yi4KCHTb0BVvbodQY5bY8eW4asMRDTxTW46hqw9lGjACX6TlLdJrkdoHYRB0qs+MekqEq1zG7WDnWE8Ug== tailwind-merge@^1.13.2: version "1.13.2" @@ -21230,11 +21064,16 @@ tslib@^1.10.0, tslib@^1.11.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, "tslib@^2.4.1 || ^1.9.3", tslib@^2.5.0, tslib@^2.6.2: +tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.2.0, tslib@^2.3.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.4.1, "tslib@^2.4.1 || ^1.9.3", tslib@^2.5.0, tslib@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== +tslib@^2.0.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0" + integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ== + tslib@~2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c" @@ -21260,7 +21099,7 @@ tunnel-agent@^0.6.0: tv4@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" - integrity sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM= + integrity sha512-afizzfpJgvPr+eDkREK4MxJ/+r8nEEHcmitwgnPUqpaP+FpwQyadnxNoSACbgc/b1LsZYtODGoPiFxQrgJgjvw== tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" @@ -21286,13 +21125,6 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -21660,7 +21492,7 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^3.2.1, uuid@^3.3.2: +uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== @@ -21788,14 +21620,6 @@ vizion@~2.2.1: ini "^1.3.5" js-git "^0.7.8" -vm2@^3.9.3: - version "3.9.18" - resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.18.tgz#d919848bee191a0410c5cc1c5aac58adfd03ce9a" - integrity sha512-iM7PchOElv6Uv6Q+0Hq7dcgDtWWT6SizYqVcvol+1WQc+E9HlgTCnPozbQNSP3yDV9oXHQOEQu530w2q/BCVZg== - dependencies: - acorn "^8.7.0" - acorn-walk "^8.2.0" - vscode-apollo-relay@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/vscode-apollo-relay/-/vscode-apollo-relay-1.5.2.tgz#0893f56ba11debf85391aefa9241e8afd356c012" @@ -22133,11 +21957,6 @@ wildcard@^2.0.0: resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== -word-wrap@~1.2.3: - version "1.2.4" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.4.tgz#cb4b50ec9aca570abd1f52f33cd45b6c61739a9f" - integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== - wordwrap@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -22342,7 +22161,7 @@ wrap-ansi@^8.1.0: wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^2.3.0, write-file-atomic@^2.4.2: version "2.4.3" @@ -22404,7 +22223,7 @@ write-pkg@^4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -ws@7.4.6, ws@~7.4.0: +ws@7.4.6: version "7.4.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== @@ -22414,7 +22233,12 @@ ws@8.13.0, ws@^8.12.0, ws@^8.13.0, ws@^8.9.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== -ws@^7.0.0, ws@^7.3.1: +ws@^7.0.0, ws@~7.5.10: + version "7.5.10" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.10.tgz#58b5c20dc281633f6c19113f39b349bd8bd558d9" + integrity sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ== + +ws@^7.3.1: version "7.5.6" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== @@ -22472,11 +22296,6 @@ xpath@0.0.32, xpath@^0.0.32: resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.32.tgz#1b73d3351af736e17ec078d6da4b8175405c48af" integrity sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw== -xregexp@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" - integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= - xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" @@ -22517,14 +22336,6 @@ yaml@^2.3.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== -yamljs@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" - integrity sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ== - dependencies: - argparse "^1.0.7" - glob "^7.0.5" - yamux-js@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yamux-js/-/yamux-js-0.1.2.tgz#a157e4922f8f0393725955c352b418f16259fd48" From 73a5881709f9345767c2a233cbe28716b6c3b8e1 Mon Sep 17 00:00:00 2001 From: Matt Krick Date: Wed, 24 Jul 2024 12:06:52 -0700 Subject: [PATCH 05/18] chore(rethinkdb): TeamMember: Phase 3 (#10003) Signed-off-by: Matt Krick --- codegen.json | 4 +- packages/server/database/rethinkDriver.ts | 5 - packages/server/database/types/TeamMember.ts | 55 ----- .../dataloader/foreignKeyLoaderMakers.ts | 17 ++ .../dataloader/primaryKeyLoaderMakers.ts | 4 + .../rethinkForeignKeyLoaderMakers.ts | 28 --- .../rethinkPrimaryKeyLoaderMakers.ts | 1 - .../server/graphql/mutations/archiveTeam.ts | 10 +- .../mutations/helpers/createTeamAndLeader.ts | 5 +- .../mutations/helpers/removeTeamMember.ts | 12 +- .../helpers/sendPokerMeetingRevoteEvent.ts | 2 +- .../server/graphql/mutations/joinMeeting.ts | 4 +- .../graphql/mutations/promoteToTeamLead.ts | 10 - .../graphql/mutations/removeTeamMember.ts | 8 +- .../graphql/mutations/setPokerSpectate.ts | 7 +- .../graphql/mutations/startSprintPoker.ts | 2 +- .../graphql/mutations/toggleTeamDrawer.ts | 15 -- .../private/mutations/changeEmailDomain.ts | 7 - .../private/mutations/hardDeleteUser.ts | 1 - .../graphql/private/mutations/updateEmail.ts | 27 +-- .../graphql/private/typeDefs/_legacy.graphql | 17 -- .../mutations/acceptRequestToJoinDomain.ts | 4 +- .../public/mutations/updateUserProfile.ts | 36 +-- .../public/mutations/uploadUserImage.ts | 31 +-- .../RepoIntegrationQueryPayload.graphql | 16 ++ .../public/typeDefs/TeamMember.graphql | 133 ++++++++++ .../graphql/public/typeDefs/_legacy.graphql | 17 -- .../types/AcceptTeamInvitationPayload.ts | 2 +- .../AddTeamMemberIntegrationAuthSuccess.ts | 2 +- .../server/graphql/public/types/AgendaItem.ts | 2 +- .../server/graphql/public/types/Company.ts | 2 +- .../public/types/JiraServerIntegration.ts | 2 +- .../server/graphql/public/types/NewMeeting.ts | 2 +- .../public/types/NotifyTaskInvolves.ts | 2 +- .../RemoveTeamMemberIntegrationAuthSuccess.ts | 2 +- .../public/types/RemoveTeamMemberPayload.ts | 2 +- packages/server/graphql/public/types/Team.ts | 2 +- .../server/graphql/public/types/TeamMember.ts | 101 ++++++++ packages/server/graphql/queries/tasks.ts | 3 +- packages/server/graphql/resolvers.ts | 2 +- packages/server/graphql/types/Team.ts | 2 +- packages/server/graphql/types/TeamMember.ts | 228 +----------------- packages/server/postgres/types/index.d.ts | 5 +- .../safeMutations/acceptTeamInvitation.ts | 2 - .../safeMutations/insertNewTeamMember.ts | 43 ---- packages/server/utils/authorization.ts | 16 -- 46 files changed, 339 insertions(+), 561 deletions(-) delete mode 100644 packages/server/database/types/TeamMember.ts create mode 100644 packages/server/graphql/public/typeDefs/RepoIntegrationQueryPayload.graphql create mode 100644 packages/server/graphql/public/typeDefs/TeamMember.graphql delete mode 100644 packages/server/safeMutations/insertNewTeamMember.ts diff --git a/codegen.json b/codegen.json index 99f5a4b9baf..14c1806ab5f 100644 --- a/codegen.json +++ b/codegen.json @@ -111,7 +111,7 @@ "NotifyTaskInvolves": "../../database/types/NotificationTaskInvolves#default", "NotifyTeamArchived": "../../database/types/NotificationTeamArchived#default", "Organization": "./types/Organization#OrganizationSource", - "OrganizationUser": "../../postgres/types/index#OrganizationUser", + "OrganizationUser": "../../postgres/types/index#OrganizationUser as OrganizationUserDB", "PokerMeeting": "../../database/types/MeetingPoker#default as MeetingPoker", "PokerMeetingMember": "../../database/types/MeetingPokerMeetingMember#default as PokerMeetingMemberDB", "PokerTemplate": "../../database/types/PokerTemplate#default as PokerTemplateDB", @@ -145,7 +145,7 @@ "TeamHealthPhase": "./types/TeamHealthPhase#TeamHealthPhaseSource", "TeamHealthStage": "./types/TeamHealthStage#TeamHealthStageSource", "TeamInvitation": "../../database/types/TeamInvitation#default", - "TeamMember": "../../database/types/TeamMember#default as TeamMemberDB", + "TeamMember": "../../postgres/types/index#TeamMember as TeamMember", "TeamMemberIntegrationAuthWebhook": "../../postgres/queries/getTeamMemberIntegrationAuth#TeamMemberIntegrationAuth", "TeamMemberIntegrationAuthOAuth1": "../../postgres/queries/getTeamMemberIntegrationAuth#TeamMemberIntegrationAuth", "TeamMemberIntegrationAuthOAuth2": "../../postgres/queries/getTeamMemberIntegrationAuth#TeamMemberIntegrationAuth", diff --git a/packages/server/database/rethinkDriver.ts b/packages/server/database/rethinkDriver.ts index a8aa3ddd709..713d255a085 100644 --- a/packages/server/database/rethinkDriver.ts +++ b/packages/server/database/rethinkDriver.ts @@ -2,7 +2,6 @@ import {MasterPool, r} from 'rethinkdb-ts' import SlackAuth from '../database/types/SlackAuth' import SlackNotification from '../database/types/SlackNotification' import TeamInvitation from '../database/types/TeamInvitation' -import TeamMember from '../database/types/TeamMember' import {AnyMeeting, AnyMeetingSettings, AnyMeetingTeamMember} from '../postgres/types/Meeting' import {ScheduledJobUnion} from '../types/custom' import getRethinkConfig from './getRethinkConfig' @@ -156,10 +155,6 @@ export type RethinkSchema = { type: TeamInvitation index: 'email' | 'teamId' | 'token' } - TeamMember: { - type: TeamMember - index: 'teamId' | 'userId' - } TemplateDimension: { type: TemplateDimension index: 'teamId' | 'templateId' | 'scaleId' diff --git a/packages/server/database/types/TeamMember.ts b/packages/server/database/types/TeamMember.ts deleted file mode 100644 index 8ca3defbeb9..00000000000 --- a/packages/server/database/types/TeamMember.ts +++ /dev/null @@ -1,55 +0,0 @@ -import toTeamMemberId from 'parabol-client/utils/relay/toTeamMemberId' -import {TeamDrawer} from './../../../client/__generated__/ToggleTeamDrawerMutation.graphql' - -interface Input { - isNotRemoved?: boolean - isLead?: boolean - isSpectatingPoker?: boolean - email: string - openDrawer?: TeamDrawer - picture: string - preferredName: string - teamId: string - userId: string - updatedAt?: Date -} - -export default class TeamMember { - id: string - isNotRemoved: boolean - isLead: boolean - isSpectatingPoker: boolean - email: string - openDrawer: TeamDrawer | null - picture: string - preferredName: string - teamId: string - userId: string - createdAt: Date - updatedAt: Date - constructor(input: Input) { - const { - teamId, - email, - isLead, - isNotRemoved, - openDrawer, - picture, - preferredName, - userId, - isSpectatingPoker - } = input - this.id = toTeamMemberId(teamId, userId) - this.teamId = teamId - this.email = email - this.openDrawer = openDrawer || null - this.isLead = isLead || false - this.isSpectatingPoker = isSpectatingPoker || false - this.isNotRemoved = isNotRemoved === undefined ? true : isNotRemoved - this.picture = picture - this.preferredName = preferredName - this.userId = userId - this.createdAt = new Date() - this.updatedAt = new Date() - } -} diff --git a/packages/server/dataloader/foreignKeyLoaderMakers.ts b/packages/server/dataloader/foreignKeyLoaderMakers.ts index 2f6cb7daf09..67ba8f37cd8 100644 --- a/packages/server/dataloader/foreignKeyLoaderMakers.ts +++ b/packages/server/dataloader/foreignKeyLoaderMakers.ts @@ -6,6 +6,23 @@ export const teamsByOrgIds = foreignKeyLoaderMaker('teams', 'orgId', (orgIds) => selectTeams().where('orgId', 'in', orgIds).where('isArchived', '=', false).execute() ) +export const teamMembersByTeamId = foreignKeyLoaderMaker('teamMembers', 'teamId', (teamIds) => + getKysely() + .selectFrom('TeamMember') + .selectAll() + .where('teamId', 'in', teamIds) + .where('isNotRemoved', '=', true) + .execute() +) + +export const teamMembersByUserId = foreignKeyLoaderMaker('teamMembers', 'userId', (userIds) => + getKysely() + .selectFrom('TeamMember') + .selectAll() + .where('userId', 'in', userIds) + .where('isNotRemoved', '=', true) + .execute() +) export const discussionsByMeetingId = foreignKeyLoaderMaker( 'discussions', 'meetingId', diff --git a/packages/server/dataloader/primaryKeyLoaderMakers.ts b/packages/server/dataloader/primaryKeyLoaderMakers.ts index 5f199227b88..10b7d3c3530 100644 --- a/packages/server/dataloader/primaryKeyLoaderMakers.ts +++ b/packages/server/dataloader/primaryKeyLoaderMakers.ts @@ -146,3 +146,7 @@ export const saml = primaryKeyLoaderMaker((ids: readonly string[]) => { export const organizationUsers = primaryKeyLoaderMaker((ids: readonly string[]) => { return getKysely().selectFrom('OrganizationUser').selectAll().where('id', 'in', ids).execute() }) + +export const teamMembers = primaryKeyLoaderMaker((ids: readonly string[]) => { + return getKysely().selectFrom('TeamMember').selectAll().where('id', 'in', ids).execute() +}) diff --git a/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts b/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts index 913c7dcc87f..1fbc779c4cf 100644 --- a/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts +++ b/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts @@ -221,31 +221,3 @@ export const teamInvitationsByTeamId = new RethinkForeignKeyLoaderMaker( .run() } ) - -export const teamMembersByTeamId = new RethinkForeignKeyLoaderMaker( - 'teamMembers', - 'teamId', - async (teamIds) => { - // tasksByUserId is expensive since we have to look up each team to check the team archive status - const r = await getRethink() - return r - .table('TeamMember') - .getAll(r.args(teamIds), {index: 'teamId'}) - .filter({isNotRemoved: true}) - .run() - } -) - -export const teamMembersByUserId = new RethinkForeignKeyLoaderMaker( - 'teamMembers', - 'userId', - async (userIds) => { - // tasksByUserId is expensive since we have to look up each team to check the team archive status - const r = await getRethink() - return r - .table('TeamMember') - .getAll(r.args(userIds), {index: 'userId'}) - .filter({isNotRemoved: true}) - .run() - } -) diff --git a/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts b/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts index ef78ee05b50..8c7f70d9be0 100644 --- a/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts +++ b/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts @@ -18,6 +18,5 @@ export const slackAuths = new RethinkPrimaryKeyLoaderMaker('SlackAuth') export const slackNotifications = new RethinkPrimaryKeyLoaderMaker('SlackNotification') export const suggestedActions = new RethinkPrimaryKeyLoaderMaker('SuggestedAction') export const tasks = new RethinkPrimaryKeyLoaderMaker('Task') -export const teamMembers = new RethinkPrimaryKeyLoaderMaker('TeamMember') export const teamInvitations = new RethinkPrimaryKeyLoaderMaker('TeamInvitation') export const templateDimensions = new RethinkPrimaryKeyLoaderMaker('TemplateDimension') diff --git a/packages/server/graphql/mutations/archiveTeam.ts b/packages/server/graphql/mutations/archiveTeam.ts index aaa5ec888f6..6b447ab44c1 100644 --- a/packages/server/graphql/mutations/archiveTeam.ts +++ b/packages/server/graphql/mutations/archiveTeam.ts @@ -1,12 +1,13 @@ import {GraphQLID, GraphQLNonNull, GraphQLObjectType} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' +import TeamMemberId from '../../../client/shared/gqlIds/TeamMemberId' import {maybeRemoveRestrictions} from '../../billing/helpers/teamLimitsCheck' import getRethink from '../../database/rethinkDriver' import NotificationTeamArchived from '../../database/types/NotificationTeamArchived' import removeMeetingTemplatesForTeam from '../../postgres/queries/removeMeetingTemplatesForTeam' import safeArchiveTeam from '../../safeMutations/safeArchiveTeam' import {analytics} from '../../utils/analytics/analytics' -import {getUserId, isSuperUser, isTeamLead, isUserOrgAdmin} from '../../utils/authorization' +import {getUserId, isSuperUser, isUserOrgAdmin} from '../../utils/authorization' import publish from '../../utils/publish' import standardError from '../../utils/standardError' import {GQLContext} from '../graphql' @@ -35,13 +36,14 @@ export default { // AUTH const viewerId = getUserId(authToken) - const [teamLead, viewer, teamToArchive] = await Promise.all([ - isTeamLead(viewerId, teamId, dataLoader), + const [teamMember, viewer, teamToArchive] = await Promise.all([ + dataLoader.get('teamMembers').load(TeamMemberId.join(teamId, viewerId)), dataLoader.get('users').loadNonNull(viewerId), dataLoader.get('teams').loadNonNull(teamId) ]) + const isTeamLead = teamMember?.isLead const isOrgAdmin = await isUserOrgAdmin(viewerId, teamToArchive.orgId, dataLoader) - if (!teamLead && !isSuperUser(authToken) && !isOrgAdmin) { + if (!isTeamLead && !isSuperUser(authToken) && !isOrgAdmin) { return standardError(new Error('Not team lead or org admin'), {userId: viewerId}) } diff --git a/packages/server/graphql/mutations/helpers/createTeamAndLeader.ts b/packages/server/graphql/mutations/helpers/createTeamAndLeader.ts index b6a3f3c650a..92ca62973de 100644 --- a/packages/server/graphql/mutations/helpers/createTeamAndLeader.ts +++ b/packages/server/graphql/mutations/helpers/createTeamAndLeader.ts @@ -9,7 +9,6 @@ import TimelineEventCreatedTeam from '../../../database/types/TimelineEventCreat import {DataLoaderInstance} from '../../../dataloader/RootDataLoader' import getKysely from '../../../postgres/getKysely' import IUser from '../../../postgres/types/IUser' -import insertNewTeamMember from '../../../safeMutations/insertNewTeamMember' interface ValidNewTeam { id: string @@ -68,9 +67,7 @@ export default async function createTeamAndLeader( .values(timelineEvent) .execute(), // add meeting settings - r.table('MeetingSettings').insert(meetingSettings).run(), - // denormalize common fields to team member - insertNewTeamMember(user, teamId, dataLoader) + r.table('MeetingSettings').insert(meetingSettings).run() ]) dataLoader.clearAll(['teams', 'users', 'teamMembers', 'timelineEvents', 'meetingSettings']) } diff --git a/packages/server/graphql/mutations/helpers/removeTeamMember.ts b/packages/server/graphql/mutations/helpers/removeTeamMember.ts index eba334bec0c..d9b92314e63 100644 --- a/packages/server/graphql/mutations/helpers/removeTeamMember.ts +++ b/packages/server/graphql/mutations/helpers/removeTeamMember.ts @@ -47,18 +47,12 @@ const removeTeamMember = async ( r.table('Task').getAll(teamId, {index: 'teamId'}).delete() ]) } else if (isLead) { + // assign new leader, remove old leader flag await pg .updateTable('TeamMember') .set(({not}) => ({isLead: not('isLead')})) .where('id', 'in', [teamMemberId, teamLeader.id]) .execute() - // assign new leader, remove old leader flag - await r({ - newTeamLead: r.table('TeamMember').get(teamLeader.id).update({ - isLead: true - }), - oldTeamLead: r.table('TeamMember').get(teamMemberId).update({isLead: false}) - }).run() } await pg @@ -68,10 +62,6 @@ const removeTeamMember = async ( .execute() // assign active tasks to the team lead const {integratedTasksToArchive, reassignedTasks} = await r({ - teamMember: r.table('TeamMember').get(teamMemberId).update({ - isNotRemoved: false, - updatedAt: now - }), integratedTasksToArchive: r .table('Task') .getAll(userId, {index: 'userId'}) diff --git a/packages/server/graphql/mutations/helpers/sendPokerMeetingRevoteEvent.ts b/packages/server/graphql/mutations/helpers/sendPokerMeetingRevoteEvent.ts index 2f736ae3770..9db1d61ab85 100644 --- a/packages/server/graphql/mutations/helpers/sendPokerMeetingRevoteEvent.ts +++ b/packages/server/graphql/mutations/helpers/sendPokerMeetingRevoteEvent.ts @@ -1,6 +1,6 @@ import Meeting from '../../../database/types/Meeting' import MeetingMember from '../../../database/types/MeetingMember' -import TeamMember from '../../../database/types/TeamMember' +import {TeamMember} from '../../../postgres/types' import {analytics} from '../../../utils/analytics/analytics' import {DataLoaderWorker} from '../../graphql' diff --git a/packages/server/graphql/mutations/joinMeeting.ts b/packages/server/graphql/mutations/joinMeeting.ts index 6528c0e37db..7be33d39e2f 100644 --- a/packages/server/graphql/mutations/joinMeeting.ts +++ b/packages/server/graphql/mutations/joinMeeting.ts @@ -10,11 +10,11 @@ import Meeting from '../../database/types/Meeting' import MeetingRetrospective from '../../database/types/MeetingRetrospective' import PokerMeetingMember from '../../database/types/PokerMeetingMember' import RetroMeetingMember from '../../database/types/RetroMeetingMember' -import TeamMember from '../../database/types/TeamMember' import TeamPromptMeetingMember from '../../database/types/TeamPromptMeetingMember' import TeamPromptResponseStage from '../../database/types/TeamPromptResponseStage' import UpdatesStage from '../../database/types/UpdatesStage' import insertDiscussions from '../../postgres/queries/insertDiscussions' +import {TeamMember} from '../../postgres/types' import {analytics} from '../../utils/analytics/analytics' import {getUserId, isTeamMember} from '../../utils/authorization' import getPhase from '../../utils/getPhase' @@ -82,7 +82,7 @@ const joinMeeting = { return {error: {message: 'Not on the team'}} } const teamMemberId = toTeamMemberId(teamId, viewerId) - const teamMember = await dataLoader.get('teamMembers').load(teamMemberId) + const teamMember = await dataLoader.get('teamMembers').loadNonNull(teamMemberId) const meetingMember = createMeetingMember(meeting, teamMember) const {errors} = await r.table('MeetingMember').insert(meetingMember).run() // if this is called concurrently, only 1 will be error free diff --git a/packages/server/graphql/mutations/promoteToTeamLead.ts b/packages/server/graphql/mutations/promoteToTeamLead.ts index 68f9513bc3d..83cd3f9b4ca 100644 --- a/packages/server/graphql/mutations/promoteToTeamLead.ts +++ b/packages/server/graphql/mutations/promoteToTeamLead.ts @@ -1,7 +1,6 @@ import {GraphQLID, GraphQLNonNull} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import TeamMemberId from '../../../client/shared/gqlIds/TeamMemberId' -import getRethink from '../../database/rethinkDriver' import getKysely from '../../postgres/getKysely' import {getUserId, isSuperUser, isUserOrgAdmin} from '../../utils/authorization' import publish from '../../utils/publish' @@ -27,7 +26,6 @@ export default { {teamId, userId}: {teamId: string; userId: string}, {authToken, dataLoader, socketId: mutatorId}: GQLContext ) { - const r = await getRethink() const pg = getKysely() const operationId = dataLoader.share() const subOptions = {mutatorId, operationId} @@ -61,14 +59,6 @@ export default { .where('id', 'in', [oldLeadTeamMemberId, promoteeOnTeam.id]) .execute() dataLoader.clearAll('teamMembers') - await r({ - teamLead: r.table('TeamMember').get(oldLeadTeamMemberId).update({ - isLead: false - }), - promotee: r.table('TeamMember').get(promoteeOnTeam.id).update({ - isLead: true - }) - }).run() const data = {teamId, oldLeaderId: oldLeadTeamMemberId, newLeaderId: promoteeOnTeam.id} publish(SubscriptionChannel.TEAM, teamId, 'PromoteToTeamLeadPayload', data, subOptions) diff --git a/packages/server/graphql/mutations/removeTeamMember.ts b/packages/server/graphql/mutations/removeTeamMember.ts index f235589a70a..1e0f4feda90 100644 --- a/packages/server/graphql/mutations/removeTeamMember.ts +++ b/packages/server/graphql/mutations/removeTeamMember.ts @@ -1,7 +1,8 @@ import {GraphQLID, GraphQLNonNull, GraphQLObjectType} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import fromTeamMemberId from 'parabol-client/utils/relay/fromTeamMemberId' -import {getUserId, isTeamLead, isUserOrgAdmin} from '../../utils/authorization' +import TeamMemberId from '../../../client/shared/gqlIds/TeamMemberId' +import {getUserId, isUserOrgAdmin} from '../../utils/authorization' import publish from '../../utils/publish' import standardError from '../../utils/standardError' import {GQLContext} from '../graphql' @@ -31,10 +32,11 @@ export default { const viewerId = getUserId(authToken) const {userId, teamId} = fromTeamMemberId(teamMemberId) const team = await dataLoader.get('teams').loadNonNull(teamId) - const [isOrgAdmin, isViewerTeamLead] = await Promise.all([ + const [isOrgAdmin, teamMember] = await Promise.all([ isUserOrgAdmin(viewerId, team.orgId, dataLoader), - isTeamLead(viewerId, teamId, dataLoader) + dataLoader.get('teamMembers').loadNonNull(TeamMemberId.join(teamId, viewerId)) ]) + const isViewerTeamLead = teamMember?.isLead const isSelf = viewerId === userId if (!isSelf) { if (!isOrgAdmin && !isViewerTeamLead) { diff --git a/packages/server/graphql/mutations/setPokerSpectate.ts b/packages/server/graphql/mutations/setPokerSpectate.ts index 7f007738d8d..61b7fbf9868 100644 --- a/packages/server/graphql/mutations/setPokerSpectate.ts +++ b/packages/server/graphql/mutations/setPokerSpectate.ts @@ -32,7 +32,6 @@ const setPokerSpectate = { const r = await getRethink() const pg = getKysely() const viewerId = getUserId(authToken) - const now = new Date() const operationId = dataLoader.share() const subOptions = {mutatorId, operationId} @@ -68,11 +67,7 @@ const setPokerSpectate = { .where('id', '=', teamMemberId) .execute() await r({ - meetingMember: r.table('MeetingMember').get(meetingMemberId).update({isSpectating}), - teamMember: r - .table('TeamMember') - .get(teamMemberId) - .update({isSpectatingPoker: isSpectating, updatedAt: now}) + meetingMember: r.table('MeetingMember').get(meetingMemberId).update({isSpectating}) }).run() dataLoader.clearAll('teamMembers') // mutate the dataLoader cache diff --git a/packages/server/graphql/mutations/startSprintPoker.ts b/packages/server/graphql/mutations/startSprintPoker.ts index b25cc5364de..bfeb3c72af1 100644 --- a/packages/server/graphql/mutations/startSprintPoker.ts +++ b/packages/server/graphql/mutations/startSprintPoker.ts @@ -163,7 +163,7 @@ export default { } const teamMemberId = toTeamMemberId(teamId, viewerId) - const teamMember = await dataLoader.get('teamMembers').load(teamMemberId) + const teamMember = await dataLoader.get('teamMembers').loadNonNull(teamMemberId) const {isSpectatingPoker} = teamMember const updates = { lastMeetingType: meetingType diff --git a/packages/server/graphql/mutations/toggleTeamDrawer.ts b/packages/server/graphql/mutations/toggleTeamDrawer.ts index aa9ae06abb2..5d922a8d718 100644 --- a/packages/server/graphql/mutations/toggleTeamDrawer.ts +++ b/packages/server/graphql/mutations/toggleTeamDrawer.ts @@ -1,6 +1,4 @@ import {GraphQLID, GraphQLNonNull} from 'graphql' -import getRethink from '../../database/rethinkDriver' -import {RValue} from '../../database/stricterR' import getKysely from '../../postgres/getKysely' import {getUserId, isTeamMember} from '../../utils/authorization' import standardError from '../../utils/standardError' @@ -27,7 +25,6 @@ const toggleTeamDrawer = { {teamId, teamDrawerType}: {teamId: string; teamDrawerType: TeamDrawer | null}, {authToken}: GQLContext ) => { - const r = await getRethink() const pg = getKysely() const viewerId = getUserId(authToken) @@ -47,18 +44,6 @@ const toggleTeamDrawer = { })) .where('id', '=', viewerTeamMemberId) .execute() - await r - .table('TeamMember') - .get(viewerTeamMemberId) - .update((teamMember: RValue) => ({ - openDrawer: r.branch( - teamMember('openDrawer').default(null).eq(teamDrawerType), - null, - teamDrawerType - ) - })) - .run() - return {teamMemberId: viewerTeamMemberId} } } diff --git a/packages/server/graphql/private/mutations/changeEmailDomain.ts b/packages/server/graphql/private/mutations/changeEmailDomain.ts index 0365503d134..fc0a3c384e9 100644 --- a/packages/server/graphql/private/mutations/changeEmailDomain.ts +++ b/packages/server/graphql/private/mutations/changeEmailDomain.ts @@ -82,13 +82,6 @@ const changeEmailDomain: MutationResolvers['changeEmailDomain'] = async ( .where('id', 'in', userIdsToUpdate) .returning('id') .execute(), - r - .table('TeamMember') - .filter((row: RDatum) => row('email').match(`@${normalizedOldDomain}$`)) - .update((row: RDatum) => ({ - email: row('email').split('@').nth(0).add(`@${normalizedNewDomain}`) - })) - .run(), r .table('Invoice') .filter((row: RDatum) => diff --git a/packages/server/graphql/private/mutations/hardDeleteUser.ts b/packages/server/graphql/private/mutations/hardDeleteUser.ts index 7bc41619d2b..9037ff5a653 100644 --- a/packages/server/graphql/private/mutations/hardDeleteUser.ts +++ b/packages/server/graphql/private/mutations/hardDeleteUser.ts @@ -87,7 +87,6 @@ const hardDeleteUser: MutationResolvers['hardDeleteUser'] = async ( .filter((row: RValue) => row('createdBy').eq(userIdToDelete)) .update({createdBy: null}) .run(), - teamMember: r.table('TeamMember').getAll(userIdToDelete, {index: 'userId'}).delete(), meetingMember: r.table('MeetingMember').getAll(userIdToDelete, {index: 'userId'}).delete(), notification: r.table('Notification').getAll(userIdToDelete, {index: 'userId'}).delete(), suggestedAction: r.table('SuggestedAction').getAll(userIdToDelete, {index: 'userId'}).delete(), diff --git a/packages/server/graphql/private/mutations/updateEmail.ts b/packages/server/graphql/private/mutations/updateEmail.ts index e06fe860c56..bd1f0766da9 100644 --- a/packages/server/graphql/private/mutations/updateEmail.ts +++ b/packages/server/graphql/private/mutations/updateEmail.ts @@ -1,10 +1,8 @@ -import getRethink from '../../../database/rethinkDriver' import getKysely from '../../../postgres/getKysely' import {getUserByEmail} from '../../../postgres/queries/getUsersByEmails' import {MutationResolvers} from '../resolverTypes' const updateEmail: MutationResolvers['updateEmail'] = async (_source, {oldEmail, newEmail}) => { - const r = await getRethink() const pg = getKysely() // VALIDATION @@ -19,23 +17,14 @@ const updateEmail: MutationResolvers['updateEmail'] = async (_source, {oldEmail, // RESOLUTION const {id: userId} = user - await Promise.all([ - pg - .with('TeamMemberUpdate', (qc) => - qc.updateTable('TeamMember').set({email: newEmail}).where('userId', '=', userId) - ) - .updateTable('User') - .set({email: newEmail}) - .where('id', '=', userId) - .execute(), - r - .table('TeamMember') - .getAll(userId, {index: 'userId'}) - .update({ - email: newEmail - }) - .run() - ]) + await pg + .with('TeamMemberUpdate', (qc) => + qc.updateTable('TeamMember').set({email: newEmail}).where('userId', '=', userId) + ) + .updateTable('User') + .set({email: newEmail}) + .where('id', '=', userId) + .execute() return true } diff --git a/packages/server/graphql/private/typeDefs/_legacy.graphql b/packages/server/graphql/private/typeDefs/_legacy.graphql index d702b048134..48da89dfeff 100644 --- a/packages/server/graphql/private/typeDefs/_legacy.graphql +++ b/packages/server/graphql/private/typeDefs/_legacy.graphql @@ -989,23 +989,6 @@ enum MeetingTypeEnum { teamPrompt } -""" -The details associated with the possible repo and project integrations -""" -type RepoIntegrationQueryPayload { - error: StandardMutationError - - """ - true if the items returned are a subset of all the possible integration, else false (all possible integrations) - """ - hasMore: Boolean! - - """ - All the integrations that are likely to be integrated - """ - items: [RepoIntegration!] -} - """ The scope of a shareable item """ diff --git a/packages/server/graphql/public/mutations/acceptRequestToJoinDomain.ts b/packages/server/graphql/public/mutations/acceptRequestToJoinDomain.ts index 33a5ada82b6..e67a1728c9f 100644 --- a/packages/server/graphql/public/mutations/acceptRequestToJoinDomain.ts +++ b/packages/server/graphql/public/mutations/acceptRequestToJoinDomain.ts @@ -6,7 +6,6 @@ import TeamMemberId from '../../../../client/shared/gqlIds/TeamMemberId' import adjustUserCount from '../../../billing/helpers/adjustUserCount' import getKysely from '../../../postgres/getKysely' import {getUserById} from '../../../postgres/queries/getUsersByIds' -import insertNewTeamMember from '../../../safeMutations/insertNewTeamMember' import {Logger} from '../../../utils/Logger' import RedisLock from '../../../utils/RedisLock' import {getUserId} from '../../../utils/authorization' @@ -108,8 +107,7 @@ const acceptRequestToJoinDomain: MutationResolvers['acceptRequestToJoinDomain'] openDrawer: 'manageTeam' }) .onConflict((oc) => oc.column('id').doUpdateSet({isNotRemoved: true})) - .execute(), - insertNewTeamMember(user, teamId, dataLoader) + .execute() ]) if (!organizationUser) { diff --git a/packages/server/graphql/public/mutations/updateUserProfile.ts b/packages/server/graphql/public/mutations/updateUserProfile.ts index 72816963520..759e6761ffa 100644 --- a/packages/server/graphql/public/mutations/updateUserProfile.ts +++ b/packages/server/graphql/public/mutations/updateUserProfile.ts @@ -1,7 +1,5 @@ import {SubscriptionChannel} from 'parabol-client/types/constEnums' import linkify from 'parabol-client/utils/linkify' -import getRethink from '../../../database/rethinkDriver' -import TeamMember from '../../../database/types/TeamMember' import getKysely from '../../../postgres/getKysely' import {analytics} from '../../../utils/analytics/analytics' import {getUserId, isAuthenticated} from '../../../utils/authorization' @@ -14,7 +12,6 @@ const updateUserProfile: MutationResolvers['updateUserProfile'] = async ( {updatedUser}, {authToken, dataLoader, socketId: mutatorId} ) => { - const r = await getRethink() const pg = getKysely() const operationId = dataLoader.share() const subOptions = {operationId, mutatorId} @@ -47,28 +44,17 @@ const updateUserProfile: MutationResolvers['updateUserProfile'] = async ( // RESOLUTION // propagate denormalized changes to TeamMember - const updateObj = { - preferredName: normalizedPreferredName - } - await Promise.all([ - pg - .with('TeamMemberUpdate', (qc) => - qc - .updateTable('TeamMember') - .set({preferredName: normalizedPreferredName}) - .where('userId', '=', userId) - ) - .updateTable('User') - .set({preferredName: normalizedPreferredName}) - .where('id', '=', userId) - .execute(), - r - .table('TeamMember') - .getAll(userId, {index: 'userId'}) - .update(updateObj, {returnChanges: true})('changes')('new_val') - .default([]) - .run() as unknown as TeamMember[] - ]) + await pg + .with('TeamMemberUpdate', (qc) => + qc + .updateTable('TeamMember') + .set({preferredName: normalizedPreferredName}) + .where('userId', '=', userId) + ) + .updateTable('User') + .set({preferredName: normalizedPreferredName}) + .where('id', '=', userId) + .execute() dataLoader.clearAll(['users', 'teamMembers']) const [user, teamMembers] = await Promise.all([ diff --git a/packages/server/graphql/public/mutations/uploadUserImage.ts b/packages/server/graphql/public/mutations/uploadUserImage.ts index 394f3053066..9208187d1f7 100644 --- a/packages/server/graphql/public/mutations/uploadUserImage.ts +++ b/packages/server/graphql/public/mutations/uploadUserImage.ts @@ -1,21 +1,18 @@ import {SubscriptionChannel} from 'parabol-client/types/constEnums' -import getRethink from '../../../database/rethinkDriver' import getFileStoreManager from '../../../fileStorage/getFileStoreManager' import normalizeAvatarUpload from '../../../fileStorage/normalizeAvatarUpload' import validateAvatarUpload from '../../../fileStorage/validateAvatarUpload' import getKysely from '../../../postgres/getKysely' -import updateUser from '../../../postgres/queries/updateUser' import {getUserId, isAuthenticated} from '../../../utils/authorization' import publish from '../../../utils/publish' import standardError from '../../../utils/standardError' -import {MutationResolvers, TeamMember} from '../resolverTypes' +import {MutationResolvers} from '../resolverTypes' const uploadUserImage: MutationResolvers['uploadUserImage'] = async ( _, {file}, {authToken, dataLoader, socketId: mutatorId} ) => { - const r = await getRethink() const pg = getKysely() const operationId = dataLoader.share() const subOptions = {operationId, mutatorId} @@ -34,24 +31,14 @@ const uploadUserImage: MutationResolvers['uploadUserImage'] = async ( const manager = getFileStoreManager() const publicLocation = await manager.putUserAvatar(normalBuffer, userId, normalExt) - await Promise.all([ - pg - .with('TeamMemberUpdate', (qc) => - qc.updateTable('TeamMember').set({picture: publicLocation}).where('userId', '=', userId) - ) - .updateTable('User') - .set({picture: publicLocation}) - .where('id', '=', userId) - .execute(), - r - .table('TeamMember') - .getAll(userId, {index: 'userId'}) - .update({picture: publicLocation}, {returnChanges: true})('changes')('new_val') - .default([]) - .run() as unknown as TeamMember[], - - updateUser({picture: publicLocation}, userId) - ]) + await pg + .with('TeamMemberUpdate', (qc) => + qc.updateTable('TeamMember').set({picture: publicLocation}).where('userId', '=', userId) + ) + .updateTable('User') + .set({picture: publicLocation}) + .where('id', '=', userId) + .execute() dataLoader.clearAll(['users', 'teamMembers']) const teamMembers = await dataLoader.get('teamMembersByUserId').load(userId) const teamIds = teamMembers.map(({teamId}) => teamId) diff --git a/packages/server/graphql/public/typeDefs/RepoIntegrationQueryPayload.graphql b/packages/server/graphql/public/typeDefs/RepoIntegrationQueryPayload.graphql new file mode 100644 index 00000000000..9f8c653ee58 --- /dev/null +++ b/packages/server/graphql/public/typeDefs/RepoIntegrationQueryPayload.graphql @@ -0,0 +1,16 @@ +""" +The details associated with the possible repo and project integrations +""" +type RepoIntegrationQueryPayload { + error: StandardMutationError + + """ + true if the items returned are a subset of all the possible integration, else false (all possible integrations) + """ + hasMore: Boolean + + """ + All the integrations that are likely to be integrated + """ + items: [RepoIntegration!] +} diff --git a/packages/server/graphql/public/typeDefs/TeamMember.graphql b/packages/server/graphql/public/typeDefs/TeamMember.graphql new file mode 100644 index 00000000000..d442dd77214 --- /dev/null +++ b/packages/server/graphql/public/typeDefs/TeamMember.graphql @@ -0,0 +1,133 @@ +""" +A member of a team +""" +type TeamMember { + """ + An ID for the teamMember. userId::teamId + """ + id: ID! + + """ + The datetime the team member was created + """ + createdAt: DateTime! + + """ + true if the user is a part of the team, false if they no longer are + """ + isNotRemoved: Boolean + + """ + Is user a team lead? + """ + isLead: Boolean! + + """ + true if the user prefers to not vote during a poker meeting + """ + isSpectatingPoker: Boolean! + + """ + the type of drawer that is open in the team dash. Null if the drawer is closed + """ + openDrawer: TeamDrawer + + """ + The user email + """ + email: Email! + + """ + true if this team member belongs to the user that queried it + """ + isSelf: Boolean! + + """ + The integrations that the team member has authorized. accessible by all + """ + integrations: TeamMemberIntegrations! + + """ + The meeting specifics for the meeting the team member is currently in + """ + meetingMember(meetingId: ID!): MeetingMember + + """ + The name of the assignee + """ + preferredName: String! + + """ + The integrations that the team has previously used + """ + prevUsedRepoIntegrations( + """ + the number of repo integrations to return + """ + first: Int! + after: DateTime + ): RepoIntegrationQueryPayload! + + """ + The integrations that the user would probably like to use + """ + repoIntegrations( + """ + the number of repo integrations to return + """ + first: Int! + after: DateTime + + """ + true if we should fetch from the network, false if we should use the cache + """ + networkOnly: Boolean! + ): RepoIntegrationQueryPayload! + + """ + Tasks owned by the team member + """ + tasks( + first: Int + + """ + the datetime cursor + """ + after: DateTime + ): TaskConnection + + """ + The team this team member belongs to + """ + team: Team + + """ + foreign key to Team table + """ + teamId: ID! + + """ + The user for the team member + """ + user: User! + + """ + foreign key to User table + """ + userId: ID! + + """ + All the integrations that the user could possibly use + """ + allAvailableRepoIntegrations: [RepoIntegration!]! + + """ + Is user an admin of the team's org? + """ + isOrgAdmin: Boolean! + + """ + url of user’s profile picture + """ + picture: URL! +} diff --git a/packages/server/graphql/public/typeDefs/_legacy.graphql b/packages/server/graphql/public/typeDefs/_legacy.graphql index a1fab0ffb5b..6550de57f6d 100644 --- a/packages/server/graphql/public/typeDefs/_legacy.graphql +++ b/packages/server/graphql/public/typeDefs/_legacy.graphql @@ -994,23 +994,6 @@ enum MeetingTypeEnum { teamPrompt } -""" -The details associated with the possible repo and project integrations -""" -type RepoIntegrationQueryPayload { - error: StandardMutationError - - """ - true if the items returned are a subset of all the possible integration, else false (all possible integrations) - """ - hasMore: Boolean! - - """ - All the integrations that are likely to be integrated - """ - items: [RepoIntegration!] -} - """ An invitation and expiration """ diff --git a/packages/server/graphql/public/types/AcceptTeamInvitationPayload.ts b/packages/server/graphql/public/types/AcceptTeamInvitationPayload.ts index 2babf18a1d4..1590a4b281b 100644 --- a/packages/server/graphql/public/types/AcceptTeamInvitationPayload.ts +++ b/packages/server/graphql/public/types/AcceptTeamInvitationPayload.ts @@ -19,7 +19,7 @@ const AcceptTeamInvitationPayload: AcceptTeamInvitationPayloadResolvers = { return teamId ? dataLoader.get('teams').loadNonNull(teamId) : null }, teamMember: async ({teamMemberId}, _args, {dataLoader}: GQLContext) => { - return teamMemberId ? dataLoader.get('teamMembers').load(teamMemberId) : null + return teamMemberId ? dataLoader.get('teamMembers').loadNonNull(teamMemberId) : null }, meeting: async ({meetingId}, _args, {dataLoader, authToken}) => { if (!meetingId) { diff --git a/packages/server/graphql/public/types/AddTeamMemberIntegrationAuthSuccess.ts b/packages/server/graphql/public/types/AddTeamMemberIntegrationAuthSuccess.ts index 24a623a2e02..5e0c14bd641 100644 --- a/packages/server/graphql/public/types/AddTeamMemberIntegrationAuthSuccess.ts +++ b/packages/server/graphql/public/types/AddTeamMemberIntegrationAuthSuccess.ts @@ -13,7 +13,7 @@ const AddTeamMemberIntegrationAuthSuccess: AddTeamMemberIntegrationAuthSuccessRe }, teamMember: ({teamId, userId}, _args, {dataLoader}) => { const teamMemberId = toTeamMemberId(teamId, userId) - return dataLoader.get('teamMembers').load(teamMemberId) + return dataLoader.get('teamMembers').loadNonNull(teamMemberId) }, user: async ({userId}, _args, {dataLoader}) => { return dataLoader.get('users').loadNonNull(userId) diff --git a/packages/server/graphql/public/types/AgendaItem.ts b/packages/server/graphql/public/types/AgendaItem.ts index 685ad8f290b..d924bba0d1a 100644 --- a/packages/server/graphql/public/types/AgendaItem.ts +++ b/packages/server/graphql/public/types/AgendaItem.ts @@ -3,7 +3,7 @@ import {AgendaItemResolvers} from '../resolverTypes' const AgendaItem: AgendaItemResolvers = { isActive: ({isActive}) => !!isActive, teamMember: async ({teamMemberId}, _args: unknown, {dataLoader}) => { - return dataLoader.get('teamMembers').load(teamMemberId) + return dataLoader.get('teamMembers').loadNonNull(teamMemberId) } } diff --git a/packages/server/graphql/public/types/Company.ts b/packages/server/graphql/public/types/Company.ts index 97ee27b50ec..d04004afed0 100644 --- a/packages/server/graphql/public/types/Company.ts +++ b/packages/server/graphql/public/types/Company.ts @@ -1,7 +1,7 @@ import getRethink from '../../../database/rethinkDriver' import {RDatum, RValue} from '../../../database/stricterR' import AuthToken from '../../../database/types/AuthToken' -import TeamMember from '../../../database/types/TeamMember' +import {TeamMember} from '../../../postgres/types' import {getUserId} from '../../../utils/authorization' import errorFilter from '../../errorFilter' import {DataLoaderWorker} from '../../graphql' diff --git a/packages/server/graphql/public/types/JiraServerIntegration.ts b/packages/server/graphql/public/types/JiraServerIntegration.ts index 029a57cbcfb..a4690b2d070 100644 --- a/packages/server/graphql/public/types/JiraServerIntegration.ts +++ b/packages/server/graphql/public/types/JiraServerIntegration.ts @@ -1,9 +1,9 @@ import IntegrationProviderId from '~/shared/gqlIds/IntegrationProviderId' import IntegrationRepoId from '~/shared/gqlIds/IntegrationRepoId' -import TeamMember from '../../../database/types/TeamMember' import JiraServerRestManager from '../../../integrations/jiraServer/JiraServerRestManager' import {IntegrationProviderJiraServer} from '../../../postgres/queries/getIntegrationProvidersByIds' import getLatestIntegrationSearchQueries from '../../../postgres/queries/getLatestIntegrationSearchQueries' +import {TeamMember} from '../../../postgres/types' import {getUserId} from '../../../utils/authorization' import standardError from '../../../utils/standardError' import {JiraServerIntegrationResolvers} from '../resolverTypes' diff --git a/packages/server/graphql/public/types/NewMeeting.ts b/packages/server/graphql/public/types/NewMeeting.ts index 030f8b42d06..d75bc19bceb 100644 --- a/packages/server/graphql/public/types/NewMeeting.ts +++ b/packages/server/graphql/public/types/NewMeeting.ts @@ -20,7 +20,7 @@ const NewMeeting: NewMeetingResolvers = { }, facilitator: ({facilitatorUserId, teamId}, _args, {dataLoader}) => { const teamMemberId = toTeamMemberId(teamId, facilitatorUserId) - return dataLoader.get('teamMembers').load(teamMemberId) + return dataLoader.get('teamMembers').loadNonNull(teamMemberId) }, locked: async ({endedAt, teamId}, _args, {authToken, dataLoader}) => { const viewerId = getUserId(authToken) diff --git a/packages/server/graphql/public/types/NotifyTaskInvolves.ts b/packages/server/graphql/public/types/NotifyTaskInvolves.ts index 3cbc20a861e..a5513dba45e 100644 --- a/packages/server/graphql/public/types/NotifyTaskInvolves.ts +++ b/packages/server/graphql/public/types/NotifyTaskInvolves.ts @@ -15,7 +15,7 @@ const NotifyTaskInvolves: NotifyTaskInvolvesResolvers = { }, changeAuthor: ({changeAuthorId}, _args, {dataLoader}) => { - return dataLoader.get('teamMembers').load(changeAuthorId) + return dataLoader.get('teamMembers').loadNonNull(changeAuthorId) }, team: ({teamId}, _args, {dataLoader}) => { diff --git a/packages/server/graphql/public/types/RemoveTeamMemberIntegrationAuthSuccess.ts b/packages/server/graphql/public/types/RemoveTeamMemberIntegrationAuthSuccess.ts index e722f61c18c..87263f1a50b 100644 --- a/packages/server/graphql/public/types/RemoveTeamMemberIntegrationAuthSuccess.ts +++ b/packages/server/graphql/public/types/RemoveTeamMemberIntegrationAuthSuccess.ts @@ -10,7 +10,7 @@ export type RemoveTeamMemberIntegrationAuthSuccessSource = { const RemoveTeamMemberIntegrationAuthSuccess: RemoveTeamMemberIntegrationAuthSuccessResolvers = { teamMember: ({teamId, userId}, _args, {dataLoader}) => { const teamMemberId = toTeamMemberId(teamId, userId) - return dataLoader.get('teamMembers').load(teamMemberId) + return dataLoader.get('teamMembers').loadNonNull(teamMemberId) }, user: async ({userId}, _args, {dataLoader}) => { return dataLoader.get('users').loadNonNull(userId) diff --git a/packages/server/graphql/public/types/RemoveTeamMemberPayload.ts b/packages/server/graphql/public/types/RemoveTeamMemberPayload.ts index 6a4b13f5920..dedf7714255 100644 --- a/packages/server/graphql/public/types/RemoveTeamMemberPayload.ts +++ b/packages/server/graphql/public/types/RemoveTeamMemberPayload.ts @@ -15,7 +15,7 @@ export type RemoveTeamMemberPayloadSource = { const RemoveTeamMemberPayload: RemoveTeamMemberPayloadResolvers = { teamMember: async ({teamMemberId}, _args, {dataLoader}: GQLContext) => { - return dataLoader.get('teamMembers').load(teamMemberId) + return dataLoader.get('teamMembers').loadNonNull(teamMemberId) }, team: async ({teamId}, _args, {dataLoader}) => { return dataLoader.get('teams').loadNonNull(teamId) diff --git a/packages/server/graphql/public/types/Team.ts b/packages/server/graphql/public/types/Team.ts index 4c13a4adc0f..d28e7a23ae5 100644 --- a/packages/server/graphql/public/types/Team.ts +++ b/packages/server/graphql/public/types/Team.ts @@ -37,7 +37,7 @@ const Team: TeamResolvers = { const viewerId = getUserId(authToken) if (!viewerId) return null const teamMemberId = toTeamMemberId(teamId, viewerId) - const teamMember = await dataLoader.get('teamMembers').load(teamMemberId) + const teamMember = await dataLoader.get('teamMembers').loadNonNull(teamMemberId) return teamMember }, isViewerOnTeam: async ({id: teamId}, _args, {authToken}) => isTeamMember(authToken, teamId), diff --git a/packages/server/graphql/public/types/TeamMember.ts b/packages/server/graphql/public/types/TeamMember.ts index 0e3f6007670..477914e7a21 100644 --- a/packages/server/graphql/public/types/TeamMember.ts +++ b/packages/server/graphql/public/types/TeamMember.ts @@ -1,3 +1,15 @@ +import ms from 'ms' +import isTaskPrivate from 'parabol-client/utils/isTaskPrivate' +import MeetingMemberId from '../../../../client/shared/gqlIds/MeetingMemberId' +import {getUserId} from '../../../utils/authorization' +import getAllRepoIntegrationsRedisKey from '../../../utils/getAllRepoIntegrationsRedisKey' +import getRedis from '../../../utils/getRedis' +import standardError from '../../../utils/standardError' +import connectionFromTasks from '../../queries/helpers/connectionFromTasks' +import fetchAllRepoIntegrations from '../../queries/helpers/fetchAllRepoIntegrations' +import getAllCachedRepoIntegrations from '../../queries/helpers/getAllCachedRepoIntegrations' +import getPrevUsedRepoIntegrations from '../../queries/helpers/getPrevUsedRepoIntegrations' +import {default as sortRepoIntegrations} from '../../queries/helpers/sortRepoIntegrations' import {TeamMemberResolvers} from '../resolverTypes' const TeamMember: TeamMemberResolvers = { @@ -8,8 +20,97 @@ const TeamMember: TeamMemberResolvers = { .load({userId, orgId: team.orgId}) return organizationUser?.role === 'ORG_ADMIN' }, + picture: async ({picture}, _args, {dataLoader}) => { return dataLoader.get('fileStoreAsset').load(picture) + }, + + isSelf: (source, _args, {authToken}) => { + const userId = getUserId(authToken) + return source.userId === userId + }, + + integrations: ({teamId, userId}) => { + return {teamId, userId} + }, + + meetingMember: async ({userId}, {meetingId}, {dataLoader}) => { + const meetingMemberId = MeetingMemberId.join(meetingId, userId) + return meetingId ? dataLoader.get('meetingMembers').load(meetingMemberId) : undefined + }, + + prevUsedRepoIntegrations: async ({teamId, userId}, {first}, context) => { + const {authToken, dataLoader} = context + const viewerId = getUserId(authToken) + if (userId !== viewerId) { + const user = await dataLoader.get('users').loadNonNull(userId) + const {tms} = user + const onTeam = authToken.tms.find((teamId) => tms.includes(teamId)) + if (!onTeam) { + return standardError(new Error('Not on same team as user'), {userId: viewerId}) + } + } + const prevUsedRepoIntegrations = await getPrevUsedRepoIntegrations(teamId) + if (!prevUsedRepoIntegrations) return {hasMore: false, items: []} + if (prevUsedRepoIntegrations.length > first) { + return {hasMore: true, items: prevUsedRepoIntegrations.slice(0, first)} + } else { + return {hasMore: false, items: prevUsedRepoIntegrations} + } + }, + + repoIntegrations: async ({teamId, userId}, {first, networkOnly}, context, info) => { + const {authToken, dataLoader} = context + const viewerId = getUserId(authToken) + if (userId !== viewerId) { + const user = await dataLoader.get('users').loadNonNull(userId) + const {tms} = user + const onTeam = authToken.tms.find((teamId) => tms.includes(teamId)) + if (!onTeam) { + return standardError(new Error('Not on same team as user'), {userId: viewerId}) + } + } + const [allCachedRepoIntegrations, prevUsedRepoIntegrations] = await Promise.all([ + getAllCachedRepoIntegrations(teamId, viewerId), + getPrevUsedRepoIntegrations(teamId) + ]) + const ignoreCache = networkOnly || !allCachedRepoIntegrations?.length + const allRepoIntegrations = ignoreCache + ? await fetchAllRepoIntegrations(teamId, userId, context, info) + : allCachedRepoIntegrations + if (ignoreCache) { + // create a new cache with newly fetched allRepoIntegrations + const redis = getRedis() + const allRepoIntegrationsKey = getAllRepoIntegrationsRedisKey(teamId, viewerId) + redis.set(allRepoIntegrationsKey, JSON.stringify(allRepoIntegrations), 'PX', ms('90d')) + } + const sortedRepoIntegrations = await sortRepoIntegrations( + allRepoIntegrations, + prevUsedRepoIntegrations + ) + if (sortedRepoIntegrations.length > first) { + return {hasMore: true, items: sortedRepoIntegrations.slice(0, first)} + } else { + return {hasMore: false, items: sortedRepoIntegrations} + } + }, + + tasks: async ({teamId, userId}, _args, {dataLoader}) => { + const allTasks = await dataLoader.get('tasksByTeamId').load(teamId) + const publicTasksForUserId = allTasks.filter((task) => { + if (task.userId !== userId) return false + if (isTaskPrivate(task.tags)) return false + return true + }) + return connectionFromTasks(publicTasksForUserId) + }, + + team: ({teamId}, _args, {dataLoader}) => { + return dataLoader.get('teams').loadNonNull(teamId) + }, + + user: ({userId}, _args, {dataLoader}) => { + return dataLoader.get('users').loadNonNull(userId) } } diff --git a/packages/server/graphql/queries/tasks.ts b/packages/server/graphql/queries/tasks.ts index ca5fd0cc4cd..ce47533ff7f 100644 --- a/packages/server/graphql/queries/tasks.ts +++ b/packages/server/graphql/queries/tasks.ts @@ -8,7 +8,6 @@ import { } from 'graphql' import isTaskPrivate from 'parabol-client/utils/isTaskPrivate' import Task from '../../database/types/Task' -import TeamMember from '../../database/types/TeamMember' import {getUserId} from '../../utils/authorization' import standardError from '../../utils/standardError' import errorFilter from '../errorFilter' @@ -32,7 +31,7 @@ const getValidUserIds = async ( ).filter(errorFilter) const teamMembersOnValidTeams = teamMembersByUserIds .flat() - .filter((teamMember: TeamMember) => validTeamIds.includes(teamMember.teamId)) + .filter((teamMember) => validTeamIds.includes(teamMember.teamId)) const teamMemberUserIds = new Set( teamMembersOnValidTeams.map(({userId}: {userId: string}) => userId) ) diff --git a/packages/server/graphql/resolvers.ts b/packages/server/graphql/resolvers.ts index 708f54a04db..1dac9a9f6fc 100644 --- a/packages/server/graphql/resolvers.ts +++ b/packages/server/graphql/resolvers.ts @@ -6,9 +6,9 @@ import GenericMeetingStage from '../database/types/GenericMeetingStage' import Meeting from '../database/types/Meeting' import Organization from '../database/types/Organization' import Task from '../database/types/Task' -import TeamMember from '../database/types/TeamMember' import User from '../database/types/User' import {Loaders} from '../dataloader/RootDataLoader' +import {TeamMember} from '../postgres/types' import {AnyMeeting} from '../postgres/types/Meeting' import {getUserId, isSuperUser, isUserBillingLeader} from '../utils/authorization' import {GQLContext} from './graphql' diff --git a/packages/server/graphql/types/Team.ts b/packages/server/graphql/types/Team.ts index f1c2ec71be7..a950e9412bd 100644 --- a/packages/server/graphql/types/Team.ts +++ b/packages/server/graphql/types/Team.ts @@ -153,7 +153,7 @@ const Team: GraphQLObjectType = new GraphQLObjectType({ if (!isTeamMember(authToken, teamId)) return false const viewerId = getUserId(authToken) const teamMemberId = toTeamMemberId(teamId, viewerId) - const teamMember = await dataLoader.get('teamMembers').load(teamMemberId) + const teamMember = await dataLoader.get('teamMembers').loadNonNull(teamMemberId) return !!teamMember.isLead } }, diff --git a/packages/server/graphql/types/TeamMember.ts b/packages/server/graphql/types/TeamMember.ts index eec91d60db0..97948c7b0ec 100644 --- a/packages/server/graphql/types/TeamMember.ts +++ b/packages/server/graphql/types/TeamMember.ts @@ -1,233 +1,9 @@ -import { - GraphQLBoolean, - GraphQLID, - GraphQLInt, - GraphQLNonNull, - GraphQLObjectType, - GraphQLString -} from 'graphql' -import ms from 'ms' -import isTaskPrivate from 'parabol-client/utils/isTaskPrivate' -import toTeamMemberId from 'parabol-client/utils/relay/toTeamMemberId' -import {getUserId} from '../../utils/authorization' -import getAllRepoIntegrationsRedisKey from '../../utils/getAllRepoIntegrationsRedisKey' -import getRedis from '../../utils/getRedis' -import standardError from '../../utils/standardError' +import {GraphQLObjectType} from 'graphql' import {GQLContext} from '../graphql' -import connectionFromTasks from '../queries/helpers/connectionFromTasks' -import fetchAllRepoIntegrations from '../queries/helpers/fetchAllRepoIntegrations' -import getAllCachedRepoIntegrations from '../queries/helpers/getAllCachedRepoIntegrations' -import getPrevUsedRepoIntegrations from '../queries/helpers/getPrevUsedRepoIntegrations' -import {default as sortRepoIntegrations} from '../queries/helpers/sortRepoIntegrations' -import {resolveTeam} from '../resolvers' -import GraphQLEmailType from './GraphQLEmailType' -import GraphQLISO8601Type from './GraphQLISO8601Type' -import RepoIntegrationQueryPayload from './RepoIntegrationQueryPayload' -import {TaskConnection} from './Task' -import Team from './Team' -import TeamDrawerEnum from './TeamDrawerEnum' -import TeamMemberIntegrations from './TeamMemberIntegrations' -import User from './User' const TeamMember = new GraphQLObjectType({ name: 'TeamMember', - description: 'A member of a team', - fields: () => ({ - id: { - type: new GraphQLNonNull(GraphQLID), - description: 'An ID for the teamMember. userId::teamId' - }, - createdAt: { - type: new GraphQLNonNull(GraphQLISO8601Type), - description: 'The datetime the team member was created' - }, - isNotRemoved: { - type: GraphQLBoolean, - description: 'true if the user is a part of the team, false if they no longer are' - }, - isLead: { - type: new GraphQLNonNull(GraphQLBoolean), - description: 'Is user a team lead?', - resolve: ({isLead}) => !!isLead - }, - isSpectatingPoker: { - type: new GraphQLNonNull(GraphQLBoolean), - description: 'true if the user prefers to not vote during a poker meeting', - resolve: ({isSpectatingPoker}) => !!isSpectatingPoker - }, - openDrawer: { - type: TeamDrawerEnum, - description: 'the type of drawer that is open in the team dash. Null if the drawer is closed' - }, - /* denormalized from User */ - email: { - type: new GraphQLNonNull(GraphQLEmailType), - description: 'The user email' - }, - isSelf: { - type: new GraphQLNonNull(GraphQLBoolean), - description: 'true if this team member belongs to the user that queried it', - resolve: (source, _args: unknown, {authToken}) => { - const userId = getUserId(authToken) - return source.userId === userId - } - }, - integrations: { - type: new GraphQLNonNull(TeamMemberIntegrations), - description: 'The integrations that the team member has authorized. accessible by all', - resolve: ({teamId, userId}) => { - return {teamId, userId} - } - }, - meetingMember: { - type: require('./MeetingMember').default, - description: 'The meeting specifics for the meeting the team member is currently in', - args: { - meetingId: { - type: new GraphQLNonNull(GraphQLID) - } - }, - resolve: async ({userId}, {meetingId}, {dataLoader}) => { - const meetingMemberId = toTeamMemberId(meetingId, userId) - return meetingId ? dataLoader.get('meetingMembers').load(meetingMemberId) : undefined - } - }, - preferredName: { - type: new GraphQLNonNull(GraphQLString), - description: 'The name of the assignee' - }, - prevUsedRepoIntegrations: { - description: 'The integrations that the team has previously used', - type: new GraphQLNonNull(RepoIntegrationQueryPayload), - args: { - first: { - type: new GraphQLNonNull(GraphQLInt), - description: 'the number of repo integrations to return' - }, - after: { - type: GraphQLISO8601Type - } - }, - resolve: async ({teamId, userId}: {teamId: string; userId: string}, {first}, context) => { - const {authToken, dataLoader} = context - const viewerId = getUserId(authToken) - if (userId !== viewerId) { - const user = await dataLoader.get('users').loadNonNull(userId) - const {tms} = user - const onTeam = authToken.tms.find((teamId) => tms.includes(teamId)) - if (!onTeam) { - return standardError(new Error('Not on same team as user'), {userId: viewerId}) - } - } - const prevUsedRepoIntegrations = await getPrevUsedRepoIntegrations(teamId) - if (!prevUsedRepoIntegrations) return [] - if (prevUsedRepoIntegrations.length > first) { - return {hasMore: true, items: prevUsedRepoIntegrations.slice(0, first)} - } else { - return {hasMore: false, items: prevUsedRepoIntegrations} - } - } - }, - repoIntegrations: { - description: 'The integrations that the user would probably like to use', - type: new GraphQLNonNull(RepoIntegrationQueryPayload), - args: { - first: { - type: new GraphQLNonNull(GraphQLInt), - description: 'the number of repo integrations to return' - }, - after: { - type: GraphQLISO8601Type - }, - networkOnly: { - type: new GraphQLNonNull(GraphQLBoolean), - description: 'true if we should fetch from the network, false if we should use the cache' - } - }, - resolve: async ( - {teamId, userId}: {teamId: string; userId: string}, - {first, networkOnly}, - context, - info - ) => { - const {authToken, dataLoader} = context - const viewerId = getUserId(authToken) - if (userId !== viewerId) { - const user = await dataLoader.get('users').loadNonNull(userId) - const {tms} = user - const onTeam = authToken.tms.find((teamId) => tms.includes(teamId)) - if (!onTeam) { - return standardError(new Error('Not on same team as user'), {userId: viewerId}) - } - } - const [allCachedRepoIntegrations, prevUsedRepoIntegrations] = await Promise.all([ - getAllCachedRepoIntegrations(teamId, viewerId), - getPrevUsedRepoIntegrations(teamId) - ]) - const ignoreCache = networkOnly || !allCachedRepoIntegrations?.length - const allRepoIntegrations = ignoreCache - ? await fetchAllRepoIntegrations(teamId, userId, context, info) - : allCachedRepoIntegrations - if (ignoreCache) { - // create a new cache with newly fetched allRepoIntegrations - const redis = getRedis() - const allRepoIntegrationsKey = getAllRepoIntegrationsRedisKey(teamId, viewerId) - redis.set(allRepoIntegrationsKey, JSON.stringify(allRepoIntegrations), 'PX', ms('90d')) - } - const sortedRepoIntegrations = await sortRepoIntegrations( - allRepoIntegrations, - prevUsedRepoIntegrations - ) - if (sortedRepoIntegrations.length > first) { - return {hasMore: true, items: sortedRepoIntegrations.slice(0, first)} - } else { - return {hasMore: false, items: sortedRepoIntegrations} - } - } - }, - tasks: { - type: TaskConnection, - description: 'Tasks owned by the team member', - args: { - first: { - type: GraphQLInt - }, - after: { - type: GraphQLISO8601Type, - description: 'the datetime cursor' - } - }, - resolve: async ({teamId, userId}, _args: unknown, {dataLoader}) => { - const allTasks = await dataLoader.get('tasksByTeamId').load(teamId) - const publicTasksForUserId = allTasks.filter((task) => { - if (task.userId !== userId) return false - if (isTaskPrivate(task.tags)) return false - return true - }) - return connectionFromTasks(publicTasksForUserId) - } - }, - team: { - type: Team, - description: 'The team this team member belongs to', - resolve: resolveTeam - }, - teamId: { - type: new GraphQLNonNull(GraphQLID), - description: 'foreign key to Team table' - }, - user: { - type: new GraphQLNonNull(User), - description: 'The user for the team member', - resolve({userId}, _args: unknown, {dataLoader}) { - return dataLoader.get('users').load(userId) - } - }, - userId: { - type: new GraphQLNonNull(GraphQLID), - description: 'foreign key to User table' - } - }) + fields: {} }) export default TeamMember diff --git a/packages/server/postgres/types/index.d.ts b/packages/server/postgres/types/index.d.ts index cdc9adef2ad..558bc238192 100644 --- a/packages/server/postgres/types/index.d.ts +++ b/packages/server/postgres/types/index.d.ts @@ -1,3 +1,6 @@ import {Selectable} from 'kysely' -import {OrganizationUser as OrganizationUserPG} from '../pg.d' +import {OrganizationUser as OrganizationUserPG, TeamMember as TeamMemberPG} from '../pg.d' + export type OrganizationUser = Selectable + +export type TeamMember = Selectable diff --git a/packages/server/safeMutations/acceptTeamInvitation.ts b/packages/server/safeMutations/acceptTeamInvitation.ts index 0be315f8a1f..587a4b68fdf 100644 --- a/packages/server/safeMutations/acceptTeamInvitation.ts +++ b/packages/server/safeMutations/acceptTeamInvitation.ts @@ -11,7 +11,6 @@ import {TeamSource} from '../graphql/public/types/Team' import getKysely from '../postgres/getKysely' import {Logger} from '../utils/Logger' import setUserTierForUserIds from '../utils/setUserTierForUserIds' -import insertNewTeamMember from './insertNewTeamMember' const handleFirstAcceptedInvitation = async ( team: TeamSource, @@ -108,7 +107,6 @@ const acceptTeamInvitation = async ( }) .onConflict((oc) => oc.column('id').doUpdateSet({isNotRemoved: true})) .execute(), - insertNewTeamMember(user, teamId, dataLoader), r .table('TeamInvitation') .getAll(teamId, {index: 'teamId'}) diff --git a/packages/server/safeMutations/insertNewTeamMember.ts b/packages/server/safeMutations/insertNewTeamMember.ts deleted file mode 100644 index 6a63ba6f0e1..00000000000 --- a/packages/server/safeMutations/insertNewTeamMember.ts +++ /dev/null @@ -1,43 +0,0 @@ -import toTeamMemberId from 'parabol-client/utils/relay/toTeamMemberId' -import getRethink from '../database/rethinkDriver' -import TeamMember from '../database/types/TeamMember' -import {DataLoaderInstance} from '../dataloader/RootDataLoader' -import IUser from '../postgres/types/IUser' - -const insertNewTeamMember = async (user: IUser, teamId: string, dataLoader: DataLoaderInstance) => { - const r = await getRethink() - const now = new Date() - const {id: userId} = user - const teamMemberId = toTeamMemberId(teamId, userId) - const teamMembers = await dataLoader.get('teamMembersByTeamId').load(teamId) - const existingTeamMember = teamMembers.find((tm) => tm.userId === userId) - const teamMemberCount = teamMembers.length - - if (!user) { - throw new Error('User does not exist') - } - if (existingTeamMember) { - existingTeamMember.isNotRemoved = true - existingTeamMember.updatedAt = now - await r.table('TeamMember').get(teamMemberId).replace(existingTeamMember).run() - dataLoader.clearAll('teamMembers') - return existingTeamMember - } - - const {picture, preferredName, email} = user - const isLead = teamMemberCount === 0 - const teamMember = new TeamMember({ - teamId, - userId, - picture, - preferredName, - email, - isLead, - openDrawer: 'manageTeam' - }) - - await r.table('TeamMember').insert(teamMember).run() - return teamMember -} - -export default insertNewTeamMember diff --git a/packages/server/utils/authorization.ts b/packages/server/utils/authorization.ts index d082ce15e78..c1aeeb41ac7 100644 --- a/packages/server/utils/authorization.ts +++ b/packages/server/utils/authorization.ts @@ -1,5 +1,3 @@ -import toTeamMemberId from 'parabol-client/utils/relay/toTeamMemberId' -import getRethink from '../database/rethinkDriver' import AuthToken from '../database/types/AuthToken' import {DataLoaderWorker} from '../graphql/graphql' import {OrganizationUser} from '../postgres/types' @@ -22,20 +20,6 @@ export const isTeamMember = (authToken: AuthToken, teamId: string) => { return Array.isArray(tms) && tms.includes(teamId) } -export const isTeamLead = async (userId: string, teamId: string, dataLoader: DataLoaderWorker) => { - const r = await getRethink() - const teamMemberId = toTeamMemberId(teamId, userId) - if (await r.table('TeamMember').get(teamMemberId)('isLead').default(false).run()) { - return true - } - - const team = await dataLoader.get('teams').loadNonNull(teamId) - const organizationUser = await dataLoader - .get('organizationUsersByUserIdOrgId') - .load({userId, orgId: team.orgId}) - return organizationUser?.role === 'ORG_ADMIN' -} - interface Options { clearCache?: boolean } From 1e6c0b49254b1911f9730f81366349d2f9562a58 Mon Sep 17 00:00:00 2001 From: "parabol-release-bot[bot]" <150284312+parabol-release-bot[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 12:14:15 -0700 Subject: [PATCH 06/18] chore(release): release v7.39.2 (#10028) Co-authored-by: parabol-release-bot[bot] <150284312+parabol-release-bot[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- packages/chronos/package.json | 4 ++-- packages/client/package.json | 2 +- packages/embedder/package.json | 2 +- packages/gql-executor/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/server/package.json | 4 ++-- 9 files changed, 24 insertions(+), 12 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index af120f4b997..a999d60cc0b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.39.1" + ".": "7.39.2" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d2ff9b9d19..09b9fa1731c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ This project adheres to [Semantic Versioning](http://semver.org/). This CHANGELOG follows conventions [outlined here](http://keepachangelog.com/). +## [7.39.2](https://github.com/ParabolInc/parabol/compare/v7.39.1...v7.39.2) (2024-07-24) + + +### Fixed + +* bump pm2 version ([#10027](https://github.com/ParabolInc/parabol/issues/10027)) ([0bb8ead](https://github.com/ParabolInc/parabol/commit/0bb8ead2adc52f64ad30ef57891791e1b3dd4ac1)) + + +### Changed + +* **rethinkdb:** TeamMember: Phase 3 ([#10003](https://github.com/ParabolInc/parabol/issues/10003)) ([73a5881](https://github.com/ParabolInc/parabol/commit/73a5881709f9345767c2a233cbe28716b6c3b8e1)) + ## [7.39.1](https://github.com/ParabolInc/parabol/compare/v7.39.0...v7.39.1) (2024-07-23) diff --git a/package.json b/package.json index 3e6a45467c2..7351040d65d 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.1", + "version": "7.39.2", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/chronos/package.json b/packages/chronos/package.json index 07db583627b..8852d449624 100644 --- a/packages/chronos/package.json +++ b/packages/chronos/package.json @@ -1,6 +1,6 @@ { "name": "chronos", - "version": "7.39.1", + "version": "7.39.2", "description": "A cron job scheduler", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/chronos#readme", @@ -25,6 +25,6 @@ }, "dependencies": { "cron": "^2.3.1", - "parabol-server": "7.39.1" + "parabol-server": "7.39.2" } } diff --git a/packages/client/package.json b/packages/client/package.json index f73ad247867..5ece805bce9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.1", + "version": "7.39.2", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/embedder/package.json b/packages/embedder/package.json index b09be582a0e..e78210071f3 100644 --- a/packages/embedder/package.json +++ b/packages/embedder/package.json @@ -1,6 +1,6 @@ { "name": "parabol-embedder", - "version": "7.39.1", + "version": "7.39.2", "description": "A service that computes embedding vectors from Parabol objects", "author": "Jordan Husney ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/embedder#readme", diff --git a/packages/gql-executor/package.json b/packages/gql-executor/package.json index 8794d4de07f..0738c1b2524 100644 --- a/packages/gql-executor/package.json +++ b/packages/gql-executor/package.json @@ -1,6 +1,6 @@ { "name": "gql-executor", - "version": "7.39.1", + "version": "7.39.2", "description": "A Stateless GraphQL Executor", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/gqlExecutor#readme", @@ -27,8 +27,8 @@ }, "dependencies": { "dd-trace": "^4.2.0", - "parabol-client": "7.39.1", - "parabol-server": "7.39.1", + "parabol-client": "7.39.2", + "parabol-server": "7.39.2", "undici": "^5.26.2" } } diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index ef1d9c5acc4..2626544317a 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -2,7 +2,7 @@ "name": "integration-tests", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.1", + "version": "7.39.2", "description": "", "main": "index.js", "scripts": { diff --git a/packages/server/package.json b/packages/server/package.json index 34ca2fd235b..e8ed5217f59 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.1", + "version": "7.39.2", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" @@ -124,7 +124,7 @@ "openai": "^4.24.1", "openapi-fetch": "^0.9.7", "oy-vey": "^0.12.1", - "parabol-client": "7.39.1", + "parabol-client": "7.39.2", "pg": "^8.5.1", "react": "^17.0.2", "react-dom": "^17.0.2", From 0c6c8e79dfbbac93362b3db640b008e32ba701b3 Mon Sep 17 00:00:00 2001 From: Matt Krick Date: Wed, 24 Jul 2024 17:10:16 -0700 Subject: [PATCH 07/18] chore(rethinkdb): TemplateScale: One-shot (#10021) Signed-off-by: Matt Krick --- codegen.json | 3 + .../components/SelectScaleDropdown.tsx | 12 +- .../AddPokerTemplateScaleMutation.ts | 1 + .../RemovePokerTemplateScaleMutation.ts | 6 +- .../handlers/handleAddPokerTemplateScale.ts | 4 +- packages/client/utils/dndNoise.ts | 1 + packages/embedder/indexing/meetingTemplate.ts | 2 +- packages/server/database/rethinkDriver.ts | 10 -- .../server/database/types/TemplateScale.ts | 48 ------ .../server/dataloader/customRedisQueries.ts | 16 -- .../dataloader/foreignKeyLoaderMakers.ts | 8 + .../dataloader/primaryKeyLoaderMakers.ts | 5 + .../rethinkForeignKeyLoaderMakers.ts | 14 -- .../rethinkPrimaryKeyLoaderMakers.ts | 1 - .../mutations/addPokerTemplateDimension.ts | 9 +- .../mutations/addPokerTemplateScale.ts | 124 ++++++++++----- .../mutations/addPokerTemplateScaleValue.ts | 70 ++++---- .../mutations/helpers/validateScaleValue.ts | 9 +- .../mutations/movePokerTemplateScaleValue.ts | 45 +++--- .../mutations/removePokerTemplateScale.ts | 20 ++- .../removePokerTemplateScaleValue.ts | 31 ++-- .../mutations/renamePokerTemplateScale.ts | 37 ++--- .../graphql/mutations/startSprintPoker.ts | 23 +-- .../updatePokerTemplateDimensionScale.ts | 2 +- .../updatePokerTemplateScaleValue.ts | 54 ++----- .../graphql/public/types/TemplateScale.ts | 31 ++++ .../graphql/public/types/TemplateScaleRef.ts | 14 ++ .../public/types/TemplateScaleValue.ts | 16 ++ packages/server/graphql/types/Team.ts | 13 +- .../server/graphql/types/TemplateScale.ts | 86 +--------- .../server/graphql/types/TemplateScaleRef.ts | 34 +--- .../graphql/types/TemplateScaleValue.ts | 34 ---- .../1614030642692_refs-for-meetings.ts | 5 +- ...698265600000_addPrioritizationTemplates.ts | 3 +- .../migrations/1721405703862_TemplateScale.ts | 149 ++++++++++++++++++ .../queries/src/insertTemplateRefQuery.sql | 11 -- .../src/insertTemplateScaleRefQuery.sql | 11 -- packages/server/postgres/select.ts | 18 +++ packages/server/postgres/types/index.d.ts | 20 ++- packages/server/utils/analytics/analytics.ts | 4 +- packages/server/utils/sortOrder.ts | 67 ++++++++ 41 files changed, 569 insertions(+), 502 deletions(-) delete mode 100644 packages/server/database/types/TemplateScale.ts create mode 100644 packages/server/graphql/public/types/TemplateScale.ts create mode 100644 packages/server/graphql/public/types/TemplateScaleRef.ts create mode 100644 packages/server/graphql/public/types/TemplateScaleValue.ts delete mode 100644 packages/server/graphql/types/TemplateScaleValue.ts create mode 100644 packages/server/postgres/migrations/1721405703862_TemplateScale.ts delete mode 100644 packages/server/postgres/queries/src/insertTemplateRefQuery.sql delete mode 100644 packages/server/postgres/queries/src/insertTemplateScaleRefQuery.sql create mode 100644 packages/server/postgres/select.ts create mode 100644 packages/server/utils/sortOrder.ts diff --git a/codegen.json b/codegen.json index 14c1806ab5f..00dd488587c 100644 --- a/codegen.json +++ b/codegen.json @@ -111,6 +111,9 @@ "NotifyTaskInvolves": "../../database/types/NotificationTaskInvolves#default", "NotifyTeamArchived": "../../database/types/NotificationTeamArchived#default", "Organization": "./types/Organization#OrganizationSource", + "TemplateScaleValue": "./types/TemplateScaleValue#TemplateScaleValueSource as TemplateScaleValueSourceDB", + "TemplateScale": "../../postgres/types/index#TemplateScale as TemplateScaleDB", + "TemplateScaleRef": "../../postgres/types/index#TemplateScaleRef as TemplateScaleRefDB", "OrganizationUser": "../../postgres/types/index#OrganizationUser as OrganizationUserDB", "PokerMeeting": "../../database/types/MeetingPoker#default as MeetingPoker", "PokerMeetingMember": "../../database/types/MeetingPokerMeetingMember#default as PokerMeetingMemberDB", diff --git a/packages/client/modules/meeting/components/SelectScaleDropdown.tsx b/packages/client/modules/meeting/components/SelectScaleDropdown.tsx index 3d7e91864de..b557015c6e2 100644 --- a/packages/client/modules/meeting/components/SelectScaleDropdown.tsx +++ b/packages/client/modules/meeting/components/SelectScaleDropdown.tsx @@ -59,6 +59,7 @@ const SelectScaleDropdown = (props: Props) => { scales { id isStarter + name ...ScaleDropdownMenuItem_scale } } @@ -70,8 +71,11 @@ const SelectScaleDropdown = (props: Props) => { const {selectedScale, team} = dimension const {id: seletedScaleId} = selectedScale const {id: teamId, scales} = team + const sortedScales = scales.toSorted((a, b) => { + return a.isStarter !== b.isStarter ? (a.isStarter ? 1 : -1) : a.name.localeCompare(b.name) + }) const defaultActiveIdx = useMemo( - () => scales.findIndex(({id}) => id === seletedScaleId), + () => sortedScales.findIndex(({id}) => id === seletedScaleId), [dimension] ) @@ -98,17 +102,17 @@ const SelectScaleDropdown = (props: Props) => { {...menuProps} defaultActiveIdx={defaultActiveIdx} > - {scales.map((scale) => ( + {sortedScales.map((scale) => ( ))} - {scales.length < Threshold.MAX_POKER_TEMPLATE_SCALES && ( + {sortedScales.length < Threshold.MAX_POKER_TEMPLATE_SCALES && ( { - const scale = await dataLoader.get('templateScales').load(scaleId) + const scale = await dataLoader.get('templateScales').loadNonNull(scaleId) const scaleValues = scale.values.map(({label}) => label).join(', ') return `${name}\n${description ?? ''}\n${scale.name}\n${scaleValues}` }) diff --git a/packages/server/database/rethinkDriver.ts b/packages/server/database/rethinkDriver.ts index 713d255a085..0e47dcf664c 100644 --- a/packages/server/database/rethinkDriver.ts +++ b/packages/server/database/rethinkDriver.ts @@ -13,7 +13,6 @@ import FailedAuthRequest from './types/FailedAuthRequest' import Invoice from './types/Invoice' import InvoiceItemHook from './types/InvoiceItemHook' import MassInvitation from './types/MassInvitation' -import MeetingTemplate from './types/MeetingTemplate' import NotificationKickedOut from './types/NotificationKickedOut' import NotificationMeetingStageTimeLimitEnd from './types/NotificationMeetingStageTimeLimitEnd' import NotificationMentioned from './types/NotificationMentioned' @@ -32,7 +31,6 @@ import SuggestedActionInviteYourTeam from './types/SuggestedActionInviteYourTeam import SuggestedActionTryTheDemo from './types/SuggestedActionTryTheDemo' import Task from './types/Task' import TemplateDimension from './types/TemplateDimension' -import TemplateScale from './types/TemplateScale' export type RethinkSchema = { AgendaItem: { @@ -118,10 +116,6 @@ export type RethinkSchema = { type: PushInvitation index: 'userId' } - MeetingTemplate: { - type: MeetingTemplate - index: 'teamId' | 'orgId' - } ScheduledJob: { type: ScheduledJobUnion index: 'runAt' | 'type' @@ -159,10 +153,6 @@ export type RethinkSchema = { type: TemplateDimension index: 'teamId' | 'templateId' | 'scaleId' } - TemplateScale: { - type: TemplateScale - index: 'teamId' - } } export type DBType = { diff --git a/packages/server/database/types/TemplateScale.ts b/packages/server/database/types/TemplateScale.ts deleted file mode 100644 index edbf61b3df5..00000000000 --- a/packages/server/database/types/TemplateScale.ts +++ /dev/null @@ -1,48 +0,0 @@ -import {PALETTE} from '../../../client/styles/paletteV3' -import {PokerCards} from '../../../client/types/constEnums' -import generateUID from '../../generateUID' -import TemplateScaleValue from './TemplateScaleValue' - -export interface TemplateScaleInput { - teamId: string - sortOrder: number - name: string - values?: TemplateScaleValue[] - parentScaleId?: string - isStarter?: boolean - removedAt?: Date -} - -const questionMarkCard = new TemplateScaleValue({ - color: PALETTE.FUSCIA_400, - label: PokerCards.QUESTION_CARD as string -}) -const passCard = new TemplateScaleValue({ - color: PALETTE.GRAPE_500, - label: PokerCards.PASS_CARD as string -}) - -export default class TemplateScale { - id: string - createdAt = new Date() - name: string - sortOrder: number - values: TemplateScaleValue[] - teamId: string - updatedAt = new Date() - parentScaleId?: string - isStarter?: boolean - removedAt?: Date - - constructor(input: TemplateScaleInput) { - const {name, sortOrder, values, teamId, parentScaleId, isStarter, removedAt} = input - this.id = generateUID() - this.sortOrder = sortOrder - this.name = name - this.values = values || [questionMarkCard, passCard] - this.teamId = teamId - this.parentScaleId = parentScaleId - this.isStarter = isStarter - this.removedAt = removedAt - } -} diff --git a/packages/server/dataloader/customRedisQueries.ts b/packages/server/dataloader/customRedisQueries.ts index a461202ca5a..8ce3cd85ac7 100644 --- a/packages/server/dataloader/customRedisQueries.ts +++ b/packages/server/dataloader/customRedisQueries.ts @@ -50,22 +50,6 @@ const customRedisQueries = { ) return publicTemplatesByType - }, - starterScales: async (teamIds: string[]) => { - const r = await getRethink() - - const starterScales = await Promise.all( - teamIds.map((teamId) => { - return r - .table('TemplateScale') - .getAll(teamId, {index: 'teamId'}) - .filter({isStarter: true}) - .filter((row: RDatum) => row('removedAt').default(null).eq(null)) - .run() - }) - ) - - return starterScales } } as const diff --git a/packages/server/dataloader/foreignKeyLoaderMakers.ts b/packages/server/dataloader/foreignKeyLoaderMakers.ts index 67ba8f37cd8..58579aceec5 100644 --- a/packages/server/dataloader/foreignKeyLoaderMakers.ts +++ b/packages/server/dataloader/foreignKeyLoaderMakers.ts @@ -1,4 +1,5 @@ import getKysely from '../postgres/getKysely' +import {selectTemplateScale} from '../postgres/select' import {foreignKeyLoaderMaker} from './foreignKeyLoaderMaker' import {selectOrganizations, selectRetroReflections, selectTeams} from './primaryKeyLoaderMakers' @@ -124,3 +125,10 @@ export const organizationUsersByOrgId = foreignKeyLoaderMaker( .execute() } ) + +export const scalesByTeamId = foreignKeyLoaderMaker('templateScales', 'teamId', async (teamIds) => { + return selectTemplateScale() + .where('teamId', 'in', teamIds) + .orderBy(['isStarter', 'name']) + .execute() +}) diff --git a/packages/server/dataloader/primaryKeyLoaderMakers.ts b/packages/server/dataloader/primaryKeyLoaderMakers.ts index 10b7d3c3530..0f9c8a053f9 100644 --- a/packages/server/dataloader/primaryKeyLoaderMakers.ts +++ b/packages/server/dataloader/primaryKeyLoaderMakers.ts @@ -7,6 +7,7 @@ import {getTeamPromptResponsesByIds} from '../postgres/queries/getTeamPromptResp import getTemplateRefsByIds from '../postgres/queries/getTemplateRefsByIds' import getTemplateScaleRefsByIds from '../postgres/queries/getTemplateScaleRefsByIds' import {getUsersByIds} from '../postgres/queries/getUsersByIds' +import {selectTemplateScale} from '../postgres/select' import {primaryKeyLoaderMaker} from './primaryKeyLoaderMaker' export const users = primaryKeyLoaderMaker(getUsersByIds) @@ -150,3 +151,7 @@ export const organizationUsers = primaryKeyLoaderMaker((ids: readonly string[]) export const teamMembers = primaryKeyLoaderMaker((ids: readonly string[]) => { return getKysely().selectFrom('TeamMember').selectAll().where('id', 'in', ids).execute() }) + +export const templateScales = primaryKeyLoaderMaker((ids: readonly string[]) => { + return selectTemplateScale().where('TemplateScale.id', 'in', ids).execute() +}) diff --git a/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts b/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts index 1fbc779c4cf..bdd7d47e595 100644 --- a/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts +++ b/packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts @@ -116,20 +116,6 @@ export const meetingMembersByUserId = new RethinkForeignKeyLoaderMaker( } ) -export const scalesByTeamId = new RethinkForeignKeyLoaderMaker( - 'templateScales', - 'teamId', - async (teamIds) => { - const r = await getRethink() - return r - .table('TemplateScale') - .getAll(r.args(teamIds), {index: 'teamId'}) - .filter((row: RDatum) => row('removedAt').default(null).eq(null)) - .orderBy('sortOrder') - .run() - } -) - export const templateDimensionsByTemplateId = new RethinkForeignKeyLoaderMaker( 'templateDimensions', 'templateId', diff --git a/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts b/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts index 8c7f70d9be0..cf77db7ba68 100644 --- a/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts +++ b/packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts @@ -13,7 +13,6 @@ export const meetingMembers = new RethinkPrimaryKeyLoaderMaker('MeetingMember') export const newMeetings = new RethinkPrimaryKeyLoaderMaker('NewMeeting') export const newFeatures = new RethinkPrimaryKeyLoaderMaker('NewFeature') export const notifications = new RethinkPrimaryKeyLoaderMaker('Notification') -export const templateScales = new RethinkPrimaryKeyLoaderMaker('TemplateScale') export const slackAuths = new RethinkPrimaryKeyLoaderMaker('SlackAuth') export const slackNotifications = new RethinkPrimaryKeyLoaderMaker('SlackNotification') export const suggestedActions = new RethinkPrimaryKeyLoaderMaker('SuggestedAction') diff --git a/packages/server/graphql/mutations/addPokerTemplateDimension.ts b/packages/server/graphql/mutations/addPokerTemplateDimension.ts index ff1d5a1e2ef..41fed4f6b99 100644 --- a/packages/server/graphql/mutations/addPokerTemplateDimension.ts +++ b/packages/server/graphql/mutations/addPokerTemplateDimension.ts @@ -54,13 +54,8 @@ const addPokerTemplateDimension = { // RESOLUTION const sortOrder = Math.max(0, ...activeDimensions.map((dimension) => dimension.sortOrder)) + 1 + dndNoise() - - const availableScales = await r - .table('TemplateScale') - .filter({teamId}) - .filter((row: RDatum) => row('removedAt').default(null).eq(null)) - .orderBy(r.desc('updatedAt')) - .run() + const rawAvailableScales = await dataLoader.get('scalesByTeamId').load(teamId) + const availableScales = rawAvailableScales.sort((a, b) => (a.updatedAt > b.updatedAt ? -1 : 1)) const defaultScaleId = availableScales.length > 0 ? availableScales.map((teamScale) => teamScale.id)[0] diff --git a/packages/server/graphql/mutations/addPokerTemplateScale.ts b/packages/server/graphql/mutations/addPokerTemplateScale.ts index fdb38242b46..f4c7f69dc91 100644 --- a/packages/server/graphql/mutations/addPokerTemplateScale.ts +++ b/packages/server/graphql/mutations/addPokerTemplateScale.ts @@ -1,12 +1,12 @@ import {GraphQLID, GraphQLNonNull} from 'graphql' -import {SubscriptionChannel, Threshold} from 'parabol-client/types/constEnums' -import dndNoise from 'parabol-client/utils/dndNoise' -import getRethink from '../../database/rethinkDriver' -import {RDatum} from '../../database/stricterR' -import TemplateScale from '../../database/types/TemplateScale' +import {PokerCards, SubscriptionChannel, Threshold} from 'parabol-client/types/constEnums' +import {PALETTE} from '../../../client/styles/paletteV3' +import generateUID from '../../generateUID' +import getKysely from '../../postgres/getKysely' import {analytics} from '../../utils/analytics/analytics' import {getUserId, isTeamMember} from '../../utils/authorization' import publish from '../../utils/publish' +import {positionAfter} from '../../utils/sortOrder' import standardError from '../../utils/standardError' import {GQLContext} from '../graphql' import AddPokerTemplateScalePayload from '../types/AddPokerTemplateScalePayload' @@ -27,7 +27,7 @@ const addPokerTemplateScale = { {parentScaleId, teamId}: {parentScaleId?: string | null; teamId: string}, {authToken, dataLoader, socketId: mutatorId}: GQLContext ) { - const r = await getRethink() + const pg = getKysely() const operationId = dataLoader.share() const subOptions = {operationId, mutatorId} const viewerId = getUserId(authToken) @@ -39,11 +39,7 @@ const addPokerTemplateScale = { // VALIDATION const [activeScales, viewer] = await Promise.all([ - r - .table('TemplateScale') - .getAll(teamId, {index: 'teamId'}) - .filter((row: RDatum) => row('removedAt').default(null).eq(null)) - .run(), + dataLoader.get('scalesByTeamId').load(teamId), dataLoader.get('users').loadNonNull(viewerId) ]) if (activeScales.length >= Threshold.MAX_POKER_TEMPLATE_SCALES) { @@ -51,12 +47,11 @@ const addPokerTemplateScale = { } // RESOLUTION - let newScale - const sortOrder = Math.max(0, ...activeScales.map((scale) => scale.sortOrder)) + 1 + dndNoise() + let newScaleId: string | undefined + let newScaleName: string + if (parentScaleId) { - const parentScale = (await dataLoader - .get('templateScales') - .load(parentScaleId)) as TemplateScale + const parentScale = await dataLoader.get('templateScales').load(parentScaleId) if (!parentScale) { return standardError(new Error('Parent scale not found'), {userId: viewerId}) } @@ -67,34 +62,79 @@ const addPokerTemplateScale = { } const {name} = parentScale const copyName = `${name} Copy` - const existingCopyCount = await r - .table('TemplateScale') - .getAll(teamId, {index: 'teamId'}) - .filter((row: RDatum) => row('removedAt').default(null).eq(null)) - .filter((row: RDatum) => row('name').match(`^${copyName}`) as any) - .count() - .run() - const newName = existingCopyCount === 0 ? copyName : `${copyName} #${existingCopyCount + 1}` - newScale = new TemplateScale({ - sortOrder, - name: newName, - teamId, - parentScaleId, - values: parentScale.values - }) + const re = new RegExp(`^${copyName}`) + const existingCopyCount = activeScales.filter(({name}) => name.match(re)).length + newScaleName = existingCopyCount === 0 ? copyName : `${copyName} #${existingCopyCount + 1}` + const res = await pg + .with('TemplateScaleInsert', (qc) => + qc + .insertInto('TemplateScale') + .values({ + id: generateUID(), + name: newScaleName, + teamId, + parentScaleId + }) + .returning(['id', 'parentScaleId']) + ) + .insertInto('TemplateScaleValue') + .columns(['templateScaleId', 'sortOrder', 'color', 'label']) + .expression(({selectFrom}) => + selectFrom('TemplateScaleValue') + .innerJoin( + 'TemplateScaleInsert', + 'TemplateScaleValue.templateScaleId', + 'TemplateScaleInsert.parentScaleId' + ) + .select(({ref}) => [ + ref('TemplateScaleInsert.id').as('templateScaleId'), + ref('TemplateScaleValue.sortOrder').as('sortOrder'), + ref('TemplateScaleValue.color').as('color'), + ref('TemplateScaleValue.label').as('label') + ]) + ) + .returning('TemplateScaleValue.templateScaleId') + .executeTakeFirstOrThrow() + newScaleId = res.templateScaleId } else { - newScale = new TemplateScale({ - sortOrder, - name: `*New Scale #${activeScales.length + 1}`, - teamId - }) + newScaleName = `*New Scale #${activeScales.length + 1}` + const res = await pg + .with('TemplateScaleInsert', (qc) => + qc + .insertInto('TemplateScale') + .values({ + id: generateUID(), + name: newScaleName, + teamId + }) + .returning('id as templateScaleId') + ) + .insertInto('TemplateScaleValue') + .values(({selectFrom}) => [ + { + templateScaleId: selectFrom('TemplateScaleInsert').select('templateScaleId'), + color: PALETTE.FUSCIA_400, + label: PokerCards.QUESTION_CARD as string, + sortOrder: positionAfter('') + }, + { + templateScaleId: selectFrom('TemplateScaleInsert').select('templateScaleId'), + color: PALETTE.GRAPE_500, + label: PokerCards.PASS_CARD as string, + sortOrder: positionAfter(positionAfter('')) + } + ]) + .returning('templateScaleId') + .executeTakeFirstOrThrow() + newScaleId = res.templateScaleId } - - await r.table('TemplateScale').insert(newScale).run() - - const scaleId = newScale.id - const data = {scaleId} - analytics.scaleMetrics(viewer, newScale, parentScaleId ? 'Scale Cloned' : 'Scale Created') + dataLoader.clearAll('templateScales') + const data = {scaleId: newScaleId} + analytics.scaleMetrics( + viewer, + {id: newScaleId, name: newScaleName, teamId}, + parentScaleId ? 'Scale Cloned' : 'Scale Created' + ) publish(SubscriptionChannel.TEAM, teamId, 'AddPokerTemplateScalePayload', data, subOptions) return data } diff --git a/packages/server/graphql/mutations/addPokerTemplateScaleValue.ts b/packages/server/graphql/mutations/addPokerTemplateScaleValue.ts index 3470e9418dc..2d379081848 100644 --- a/packages/server/graphql/mutations/addPokerTemplateScaleValue.ts +++ b/packages/server/graphql/mutations/addPokerTemplateScaleValue.ts @@ -1,20 +1,15 @@ import {GraphQLID, GraphQLNonNull} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import getRethink from '../../database/rethinkDriver' -import {RDatum} from '../../database/stricterR' -import TemplateScale from '../../database/types/TemplateScale' import getKysely from '../../postgres/getKysely' import {getUserId, isTeamMember} from '../../utils/authorization' import publish from '../../utils/publish' +import {getSortOrder} from '../../utils/sortOrder' import standardError from '../../utils/standardError' import {GQLContext} from '../graphql' import AddPokerTemplateScaleValuePayload from '../types/AddPokerTemplateScaleValuePayload' import AddTemplateScaleInput, {AddTemplateScaleInputType} from '../types/AddTemplateScaleInput' -import { - validateColorValue, - validateScaleLabel, - validateScaleLabelValueUniqueness -} from './helpers/validateScaleValue' +import {validateColorValue, validateScaleLabel} from './helpers/validateScaleValue' const addPokerTemplateScaleValue = { description: 'Add a new scale value for a scale in a poker template', @@ -40,7 +35,7 @@ const addPokerTemplateScaleValue = { const viewerId = getUserId(authToken) // AUTH - const existingScale = await r.table('TemplateScale').get(scaleId).run() + const existingScale = await dataLoader.get('templateScales').load(scaleId) if (!existingScale || existingScale.removedAt) { return standardError(new Error('Did not find an active scale'), {userId: viewerId}) } @@ -56,48 +51,39 @@ const addPokerTemplateScaleValue = { if (!validateScaleLabel(label)) { return standardError(new Error('Invalid scale label'), {userId: viewerId}) } - - const updatedScale = await r - .table('TemplateScale') - .get(scaleId) - .update( - (row: RDatum) => ({ - // Append at the end of the sub-array (minus ? and Pass) - values: row('values').insertAt(row('values').count().sub(2), scaleValue), - updatedAt: now - }), - {returnChanges: true} - )('changes')(0)('new_val') - .default(null) - .run() - - if (updatedScale && !validateScaleLabelValueUniqueness(updatedScale.values)) { - // updated values and/or labels are not unique, rolling back - await r - .table('TemplateScale') - .get(scaleId) - .update({ - values: existingScale.values, - updatedAt: existingScale.updatedAt + const {values} = existingScale + const endCardIdx = values.findIndex(({label}) => ['?', 'Pass'].includes(label)) + const sortOrder = getSortOrder(values, endCardIdx + 1, endCardIdx) + try { + await pg + .insertInto('TemplateScaleValue') + .values({ + templateScaleId: scaleId, + color, + label, + sortOrder }) - .run() - return standardError(new Error('Scale labels and/or numerical values are not unique'), { - userId: viewerId - }) + .execute() + } catch (e) { + if ((e as any).constraint === 'TemplateScaleValue_templateScaleId_label_key') { + return {error: {message: 'Scale labels and/or numerical values are not unique'}} + } + return {error: {message: 'Could not add scale value'}} } - + dataLoader.clearAll('templateScales') // mark all templates using this scale as updated const updatedDimensions = await r .table('TemplateDimension') .getAll(scaleId, {index: 'scaleId'}) .run() const updatedTemplateIds = updatedDimensions.map(({templateId}) => templateId) - await pg - .updateTable('MeetingTemplate') - .set({updatedAt: now}) - .where('id', 'in', updatedTemplateIds) - .execute() - + if (updatedTemplateIds.length) { + await pg + .updateTable('MeetingTemplate') + .set({updatedAt: now}) + .where('id', 'in', updatedTemplateIds) + .execute() + } const data = {scaleId} publish( SubscriptionChannel.TEAM, diff --git a/packages/server/graphql/mutations/helpers/validateScaleValue.ts b/packages/server/graphql/mutations/helpers/validateScaleValue.ts index 908c19fb3d1..60fc93e91ff 100644 --- a/packages/server/graphql/mutations/helpers/validateScaleValue.ts +++ b/packages/server/graphql/mutations/helpers/validateScaleValue.ts @@ -1,7 +1,6 @@ import toArray from 'lodash.toarray' import palettePickerOptions from '../../../../client/styles/palettePickerOptions' import {Threshold} from '../../../../client/types/constEnums' -import TemplateScaleValue from '../../../database/types/TemplateScaleValue' const validateColorValue = (color: string) => { const validHexes = palettePickerOptions.map(({hex}) => hex) @@ -14,10 +13,4 @@ const validateScaleLabel = (label: string) => { return 0 < labelArr.length && labelArr.length <= Threshold.POKER_SCALE_VALUE_MAX_LENGTH } -const validateScaleLabelValueUniqueness = (scaleValues: TemplateScaleValue[]) => { - const labelList = scaleValues.map((scaleValue) => scaleValue.label) - - return new Set(labelList).size === labelList.length -} - -export {validateColorValue, validateScaleLabel, validateScaleLabelValueUniqueness} +export {validateColorValue, validateScaleLabel} diff --git a/packages/server/graphql/mutations/movePokerTemplateScaleValue.ts b/packages/server/graphql/mutations/movePokerTemplateScaleValue.ts index 6da4d67829d..d445953c1ed 100644 --- a/packages/server/graphql/mutations/movePokerTemplateScaleValue.ts +++ b/packages/server/graphql/mutations/movePokerTemplateScaleValue.ts @@ -1,10 +1,10 @@ import {GraphQLID, GraphQLInt, GraphQLNonNull, GraphQLString} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import getRethink from '../../database/rethinkDriver' -import {RValue} from '../../database/stricterR' import getKysely from '../../postgres/getKysely' import {getUserId, isTeamMember} from '../../utils/authorization' import publish from '../../utils/publish' +import {getSortOrder} from '../../utils/sortOrder' import standardError from '../../utils/standardError' import {GQLContext} from '../graphql' import MovePokerTemplateScaleValuePayload from '../types/MovePokerTemplateScaleValuePayload' @@ -36,7 +36,7 @@ const movePokerTemplateScaleValue = { const now = new Date() const operationId = dataLoader.share() const subOptions = {mutatorId, operationId} - const scale = await r.table('TemplateScale').get(scaleId).run() + const scale = await dataLoader.get('templateScales').load(scaleId) //AUTH if (!scale || scale.removedAt) { @@ -47,39 +47,38 @@ const movePokerTemplateScaleValue = { } // VALIDATION - if (index < 0 || index >= scale.values.length - 2) { - return standardError(new Error('Invalid index to move to'), {userId: viewerId}) - } - const scaleValueIndex = scale.values.findIndex((scaleValue) => scaleValue.label === label) - if (scaleValueIndex === -1) { + const itemIdx = scale.values.findIndex((scaleValue) => scaleValue.label === label) + if (itemIdx === -1) { return standardError(new Error('Did not find an existing scale value to move'), { userId: viewerId }) } + if (index < 0 || index >= scale.values.length - 2) { + return standardError(new Error('Invalid index to move to'), {userId: viewerId}) + } // RESOLUTION - await r - .table('TemplateScale') - .get(scaleId) - .update((row: RValue) => ({ - values: row('values') - .deleteAt(scaleValueIndex) - .insertAt(index, scale.values[scaleValueIndex]), - updatedAt: now - })) - .run() + const sortOrder = getSortOrder(scale.values, itemIdx, index) + await pg + .updateTable('TemplateScaleValue') + .set({sortOrder}) + .where('templateScaleId', '=', scale.id) + .where('label', '=', label) + .execute() + dataLoader.clearAll('templateScales') // mark all templates using this scale as updated const updatedDimensions = await r .table('TemplateDimension') .getAll(scaleId, {index: 'scaleId'}) .run() const updatedTemplateIds = updatedDimensions.map(({templateId}) => templateId) - await pg - .updateTable('MeetingTemplate') - .set({updatedAt: now}) - .where('id', 'in', updatedTemplateIds) - .execute() - + if (updatedTemplateIds.length) { + await pg + .updateTable('MeetingTemplate') + .set({updatedAt: now}) + .where('id', 'in', updatedTemplateIds) + .execute() + } const data = {scaleId} publish( SubscriptionChannel.TEAM, diff --git a/packages/server/graphql/mutations/removePokerTemplateScale.ts b/packages/server/graphql/mutations/removePokerTemplateScale.ts index b56255592f4..fd2b48a35f4 100644 --- a/packages/server/graphql/mutations/removePokerTemplateScale.ts +++ b/packages/server/graphql/mutations/removePokerTemplateScale.ts @@ -1,4 +1,5 @@ import {GraphQLID, GraphQLNonNull} from 'graphql' +import {sql} from 'kysely' import {SprintPokerDefaults, SubscriptionChannel} from 'parabol-client/types/constEnums' import getRethink from '../../database/rethinkDriver' import {RDatum} from '../../database/stricterR' @@ -40,7 +41,11 @@ const removePokerTemplateScale = { } // RESOLUTION - await r.table('TemplateScale').get(scaleId).update({removedAt: now, updatedAt: now}).run() + await pg + .updateTable('TemplateScale') + .set({removedAt: sql`CURRENT_TIMESTAMP`}) + .where('id', '=', scaleId) + .execute() const nextDefaultScaleId = SprintPokerDefaults.DEFAULT_SCALE_ID const dimensions = await r @@ -60,12 +65,13 @@ const removePokerTemplateScale = { .run() // mark templates as updated const updatedTemplateIds = dimensions.map(({templateId}: any) => templateId) - await pg - .updateTable('MeetingTemplate') - .set({updatedAt: now}) - .where('id', 'in', updatedTemplateIds) - .execute() - + if (updatedTemplateIds.length) { + await pg + .updateTable('MeetingTemplate') + .set({updatedAt: now}) + .where('id', 'in', updatedTemplateIds) + .execute() + } const data = {scaleId, dimensions} publish(SubscriptionChannel.TEAM, teamId, 'RemovePokerTemplateScalePayload', data, subOptions) return data diff --git a/packages/server/graphql/mutations/removePokerTemplateScaleValue.ts b/packages/server/graphql/mutations/removePokerTemplateScaleValue.ts index 9caecbfeab5..739ed5ce0ea 100644 --- a/packages/server/graphql/mutations/removePokerTemplateScaleValue.ts +++ b/packages/server/graphql/mutations/removePokerTemplateScaleValue.ts @@ -1,7 +1,6 @@ import {GraphQLID, GraphQLNonNull, GraphQLString} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import getRethink from '../../database/rethinkDriver' -import {RValue} from '../../database/stricterR' import getKysely from '../../postgres/getKysely' import {getUserId, isTeamMember} from '../../utils/authorization' import publish from '../../utils/publish' @@ -33,7 +32,7 @@ const removePokerTemplateScaleValue = { const viewerId = getUserId(authToken) // AUTH - const scale = await r.table('TemplateScale').get(scaleId).run() + const scale = await dataLoader.get('templateScales').load(scaleId) if (!scale || scale.removedAt) { return standardError(new Error('Did not find an active scale'), {userId: viewerId}) } @@ -51,27 +50,25 @@ const removePokerTemplateScaleValue = { } // RESOLUTION - await r - .table('TemplateScale') - .get(scaleId) - .update((row: RValue) => ({ - values: row('values').deleteAt(row('values').offsetsOf(oldScaleValue).nth(0)), - updatedAt: now - })) - .run() - + await pg + .deleteFrom('TemplateScaleValue') + .where('templateScaleId', '=', scaleId) + .where('label', '=', label) + .execute() + dataLoader.clearAll('templateScales') // mark all templates using this scale as updated const updatedDimensions = await r .table('TemplateDimension') .getAll(scaleId, {index: 'scaleId'}) .run() const updatedTemplateIds = updatedDimensions.map(({templateId}) => templateId) - await pg - .updateTable('MeetingTemplate') - .set({updatedAt: now}) - .where('id', 'in', updatedTemplateIds) - .execute() - + if (updatedTemplateIds.length) { + await pg + .updateTable('MeetingTemplate') + .set({updatedAt: now}) + .where('id', 'in', updatedTemplateIds) + .execute() + } const data = {scaleId} publish( SubscriptionChannel.TEAM, diff --git a/packages/server/graphql/mutations/renamePokerTemplateScale.ts b/packages/server/graphql/mutations/renamePokerTemplateScale.ts index f9b42c50778..6d2ce4eea06 100644 --- a/packages/server/graphql/mutations/renamePokerTemplateScale.ts +++ b/packages/server/graphql/mutations/renamePokerTemplateScale.ts @@ -1,7 +1,6 @@ import {GraphQLID, GraphQLNonNull, GraphQLString} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import getRethink from '../../database/rethinkDriver' -import {RDatum} from '../../database/stricterR' import getKysely from '../../postgres/getKysely' import {getUserId, isTeamMember} from '../../utils/authorization' import publish from '../../utils/publish' @@ -30,7 +29,7 @@ const renamePokerTemplateScale = { const now = new Date() const operationId = dataLoader.share() const subOptions = {operationId, mutatorId} - const scale = await r.table('TemplateScale').get(scaleId).run() + const scale = await dataLoader.get('templateScales').load(scaleId) const viewerId = getUserId(authToken) // AUTH @@ -46,37 +45,31 @@ const renamePokerTemplateScale = { const trimmedName = name.trim().slice(0, 50) const normalizedName = trimmedName || 'Unnamed Scale' - const allScales = await r - .table('TemplateScale') - .getAll(teamId, {index: 'teamId'}) - .filter((row: RDatum) => row('removedAt').default(null).eq(null)) - .run() + const allScales = await dataLoader.get('scalesByTeamId').load(teamId) if (allScales.find((scale) => scale.name === normalizedName)) { return standardError(new Error('Duplicate name scale'), {userId: viewerId}) } // RESOLUTION - await r - .table('TemplateScale') - .get(scaleId) - .update({ - name: normalizedName, - updatedAt: now - }) - .run() - + await pg + .updateTable('TemplateScale') + .set({name: normalizedName}) + .where('id', '=', scaleId) + .execute() + dataLoader.clearAll('templateScales') // mark all templates using this scale as updated const updatedDimensions = await r .table('TemplateDimension') .getAll(scaleId, {index: 'scaleId'}) .run() const updatedTemplateIds = updatedDimensions.map(({templateId}) => templateId) - await pg - .updateTable('MeetingTemplate') - .set({updatedAt: now}) - .where('id', 'in', updatedTemplateIds) - .execute() - + if (updatedTemplateIds.length) { + await pg + .updateTable('MeetingTemplate') + .set({updatedAt: now}) + .where('id', 'in', updatedTemplateIds) + .execute() + } const data = {scaleId} publish(SubscriptionChannel.TEAM, teamId, 'RenamePokerTemplateScalePayload', data, subOptions) return data diff --git a/packages/server/graphql/mutations/startSprintPoker.ts b/packages/server/graphql/mutations/startSprintPoker.ts index bfeb3c72af1..3b7b7766c32 100644 --- a/packages/server/graphql/mutations/startSprintPoker.ts +++ b/packages/server/graphql/mutations/startSprintPoker.ts @@ -6,9 +6,7 @@ import MeetingPoker from '../../database/types/MeetingPoker' import MeetingSettingsPoker from '../../database/types/MeetingSettingsPoker' import PokerMeetingMember from '../../database/types/PokerMeetingMember' import generateUID from '../../generateUID' -import getPg from '../../postgres/getPg' -import {insertTemplateRefQuery} from '../../postgres/queries/generated/insertTemplateRefQuery' -import {insertTemplateScaleRefQuery} from '../../postgres/queries/generated/insertTemplateScaleRefQuery' +import getKysely from '../../postgres/getKysely' import updateMeetingTemplateLastUsedAt from '../../postgres/queries/updateMeetingTemplateLastUsedAt' import updateTeamByTeamId from '../../postgres/queries/updateTeamByTeamId' import {MeetingTypeEnum} from '../../postgres/types/Meeting' @@ -27,7 +25,7 @@ import isStartMeetingLocked from './helpers/isStartMeetingLocked' import {IntegrationNotifier} from './helpers/notifications/IntegrationNotifier' const freezeTemplateAsRef = async (templateId: string, dataLoader: DataLoaderWorker) => { - const pg = getPg() + const pg = getKysely() const [template, dimensions] = await Promise.all([ dataLoader.get('meetingTemplates').loadNonNull(templateId), dataLoader.get('templateDimensionsByTemplateId').load(templateId) @@ -39,7 +37,7 @@ const freezeTemplateAsRef = async (templateId: string, dataLoader: DataLoaderWor isValid ) const templateScales = uniqueScales.map(({name, values}) => { - const scale = {name, values} + const scale = {name, values: values.map(({color, label}) => ({color, label}))} const {id, str} = getHashAndJSON(scale) return {id, scale: str} }) @@ -59,10 +57,17 @@ const freezeTemplateAsRef = async (templateId: string, dataLoader: DataLoaderWor } const {id: templateRefId, str: templateRefStr} = getHashAndJSON(templateRef) const ref = {id: templateRefId, template: templateRefStr} - await Promise.all([ - insertTemplateScaleRefQuery.run({templateScales}, pg), - insertTemplateRefQuery.run({ref}, pg) - ]) + await pg + .with('TemplateScaleRefUpsert', (qc) => + qc + .insertInto('TemplateScaleRef') + .values(templateScales) + .onConflict((oc) => oc.doNothing()) + ) + .insertInto('TemplateRef') + .values(ref) + .onConflict((oc) => oc.doNothing()) + .execute() return templateRefId } diff --git a/packages/server/graphql/mutations/updatePokerTemplateDimensionScale.ts b/packages/server/graphql/mutations/updatePokerTemplateDimensionScale.ts index 479eba4abfb..af6c5dc86d3 100644 --- a/packages/server/graphql/mutations/updatePokerTemplateDimensionScale.ts +++ b/packages/server/graphql/mutations/updatePokerTemplateDimensionScale.ts @@ -42,7 +42,7 @@ const updatePokerTemplateDimensionScale = { } // VALIDATION - const scale = await r.table('TemplateScale').get(scaleId).run() + const scale = await dataLoader.get('templateScales').load(scaleId) if (!scale || scale.removedAt || (!scale.isStarter && scale.teamId !== teamId)) { return standardError(new Error('Scale not found'), {userId: viewerId}) } diff --git a/packages/server/graphql/mutations/updatePokerTemplateScaleValue.ts b/packages/server/graphql/mutations/updatePokerTemplateScaleValue.ts index 64ce07cab26..b19dbd65478 100644 --- a/packages/server/graphql/mutations/updatePokerTemplateScaleValue.ts +++ b/packages/server/graphql/mutations/updatePokerTemplateScaleValue.ts @@ -2,7 +2,6 @@ import {GraphQLID, GraphQLNonNull} from 'graphql' import {SubscriptionChannel} from 'parabol-client/types/constEnums' import isSpecialPokerLabel from 'parabol-client/utils/isSpecialPokerLabel' import getRethink from '../../database/rethinkDriver' -import {RValue} from '../../database/stricterR' import getKysely from '../../postgres/getKysely' import {getUserId, isTeamMember} from '../../utils/authorization' import publish from '../../utils/publish' @@ -10,11 +9,7 @@ import standardError from '../../utils/standardError' import {GQLContext} from '../graphql' import TemplateScaleInput, {TemplateScaleInputType} from '../types/TemplateScaleInput' import UpdatePokerTemplateScaleValuePayload from '../types/UpdatePokerTemplateScaleValuePayload' -import { - validateColorValue, - validateScaleLabel, - validateScaleLabelValueUniqueness -} from './helpers/validateScaleValue' +import {validateColorValue, validateScaleLabel} from './helpers/validateScaleValue' const updatePokerTemplateScaleValue = { description: 'Update the label, numerical value or color of a scale value in a scale', @@ -51,7 +46,7 @@ const updatePokerTemplateScaleValue = { const viewerId = getUserId(authToken) // AUTH - const existingScale = await r.table('TemplateScale').get(scaleId).run() + const existingScale = await dataLoader.get('templateScales').load(scaleId) if (!existingScale || existingScale.removedAt) { return standardError(new Error('Did not find an active scale'), {userId: viewerId}) } @@ -83,31 +78,13 @@ const updatePokerTemplateScaleValue = { if (!isSpecialPokerLabel(label) && !validateScaleLabel(label)) { return standardError(new Error('Invalid scale label'), {userId: viewerId}) } - - await r - .table('TemplateScale') - .get(scaleId) - .update((row: RValue) => ({ - values: row('values').changeAt(oldScaleValueIndex, newScaleValue), - updatedAt: now - })) - .run() - const updatedScale = await r.table('TemplateScale').get(scaleId).run() - - if (!validateScaleLabelValueUniqueness(updatedScale.values)) { - // updated values or labels are not unique, rolling back - await r - .table('TemplateScale') - .get(scaleId) - .update({ - values: existingScale.values, - updatedAt: existingScale.updatedAt - }) - .run() - return standardError(new Error('Scale labels and/or numerical values are not unique'), { - userId: viewerId - }) - } + await pg + .updateTable('TemplateScaleValue') + .set({label, color}) + .where('templateScaleId', '=', scaleId) + .where('label', '=', oldScaleLabel) + .execute() + dataLoader.clearAll('templateScales') // mark all templates using this scale as updated const updatedDimensions = await r @@ -115,12 +92,13 @@ const updatePokerTemplateScaleValue = { .getAll(scaleId, {index: 'scaleId'}) .run() const updatedTemplateIds = updatedDimensions.map(({templateId}) => templateId) - await pg - .updateTable('MeetingTemplate') - .set({updatedAt: now}) - .where('id', 'in', updatedTemplateIds) - .execute() - + if (updatedTemplateIds.length) { + await pg + .updateTable('MeetingTemplate') + .set({updatedAt: now}) + .where('id', 'in', updatedTemplateIds) + .execute() + } const data = {scaleId} publish( SubscriptionChannel.TEAM, diff --git a/packages/server/graphql/public/types/TemplateScale.ts b/packages/server/graphql/public/types/TemplateScale.ts new file mode 100644 index 00000000000..44b3e15b292 --- /dev/null +++ b/packages/server/graphql/public/types/TemplateScale.ts @@ -0,0 +1,31 @@ +import getRethink from '../../../database/rethinkDriver' +import {RDatum} from '../../../database/stricterR' +import {TemplateScaleResolvers} from '../resolverTypes' + +const TemplateScale: TemplateScaleResolvers = { + isActive: ({removedAt}) => !removedAt, + team: ({teamId}, _args, {dataLoader}) => { + return dataLoader.get('teams').loadNonNull(teamId) + }, + + dimensions: async ({id: scaleId, teamId}) => { + const r = await getRethink() + return r + .table('TemplateDimension') + .getAll(teamId, {index: 'teamId'}) + .filter((row: RDatum) => + row('removedAt').default(null).eq(null).and(row('scaleId').eq(scaleId)) + ) + .run() + }, + + values: ({id, values}) => { + return values.map((value, index) => ({ + ...value, + scaleId: id, + sortOrder: index + })) + } +} + +export default TemplateScale diff --git a/packages/server/graphql/public/types/TemplateScaleRef.ts b/packages/server/graphql/public/types/TemplateScaleRef.ts new file mode 100644 index 00000000000..97280282825 --- /dev/null +++ b/packages/server/graphql/public/types/TemplateScaleRef.ts @@ -0,0 +1,14 @@ +import {TemplateScaleRefResolvers} from '../resolverTypes' + +const TemplateScaleRef: TemplateScaleRefResolvers = { + values: ({id, values}) => { + return values.map(({color, label}, index) => ({ + color, + label, + scaleId: id, + sortOrder: index + })) + } +} + +export default TemplateScaleRef diff --git a/packages/server/graphql/public/types/TemplateScaleValue.ts b/packages/server/graphql/public/types/TemplateScaleValue.ts new file mode 100644 index 00000000000..4e3ac6836f0 --- /dev/null +++ b/packages/server/graphql/public/types/TemplateScaleValue.ts @@ -0,0 +1,16 @@ +import {TemplateScaleValueResolvers} from '../resolverTypes' + +export type TemplateScaleValueSource = { + color: string + label: string + sortOrder: number + scaleId: string +} + +const TemplateScaleValue: TemplateScaleValueResolvers = { + id: ({scaleId, label}) => { + return `${scaleId}:${label}` + } +} + +export default TemplateScaleValue diff --git a/packages/server/graphql/types/Team.ts b/packages/server/graphql/types/Team.ts index a950e9412bd..f6bf14bb400 100644 --- a/packages/server/graphql/types/Team.ts +++ b/packages/server/graphql/types/Team.ts @@ -13,9 +13,9 @@ import getRethink from '../../database/rethinkDriver' import MassInvitationDB from '../../database/types/MassInvitation' import Task from '../../database/types/Task' import ITeam from '../../database/types/Team' -import db from '../../db' import {getUserId, isSuperUser, isTeamMember, isUserBillingLeader} from '../../utils/authorization' import standardError from '../../utils/standardError' +import isValid from '../isValid' import connectionFromTasks from '../queries/helpers/connectionFromTasks' import {GQLContext} from './../graphql' import AgendaItem from './AgendaItem' @@ -204,13 +204,10 @@ const Team: GraphQLObjectType = new GraphQLObjectType({ type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(TemplateScale))), description: 'The list of scales this team can use', resolve: async ({id: teamId}: {id: string}, {}, {dataLoader}: GQLContext) => { - const activeTeamScales = await dataLoader.get('scalesByTeamId').load(teamId) - const publicScales = await db.read('starterScales', 'aGhostTeam') - const activeScales = [...activeTeamScales, ...publicScales] - const uniqueScales = activeScales.filter( - (scale, index) => index === activeScales.findIndex((obj) => obj.id === scale.id) - ) - return uniqueScales + const availableScales = await dataLoader + .get('scalesByTeamId') + .loadMany([teamId, 'aGhostTeam']) + return availableScales.filter(isValid).flat() } }, activeMeetings: { diff --git a/packages/server/graphql/types/TemplateScale.ts b/packages/server/graphql/types/TemplateScale.ts index 0f42590aba3..66859e8a47c 100644 --- a/packages/server/graphql/types/TemplateScale.ts +++ b/packages/server/graphql/types/TemplateScale.ts @@ -1,88 +1,8 @@ -import { - GraphQLBoolean, - GraphQLID, - GraphQLList, - GraphQLNonNull, - GraphQLObjectType, - GraphQLString -} from 'graphql' -import getRethink from '../../database/rethinkDriver' -import {RDatum} from '../../database/stricterR' -import TemplateScaleDB from '../../database/types/TemplateScale' -import {GQLContext} from '../graphql' -import {resolveTeam} from '../resolvers' -import GraphQLISO8601Type from './GraphQLISO8601Type' -import Team from './Team' -import TemplateDimension from './TemplateDimension' -import TemplateScaleValue from './TemplateScaleValue' +import {GraphQLObjectType} from 'graphql' -const TemplateScale = new GraphQLObjectType({ +const TemplateScale = new GraphQLObjectType({ name: 'TemplateScale', - description: 'A team-specific template scale.', - fields: () => ({ - id: { - type: new GraphQLNonNull(GraphQLID), - description: 'shortid' - }, - createdAt: { - type: new GraphQLNonNull(GraphQLISO8601Type) - }, - isActive: { - type: new GraphQLNonNull(GraphQLBoolean), - resolve: ({removedAt}) => !removedAt, - description: 'true if the scale is currently used by the team, else false' - }, - isStarter: { - type: new GraphQLNonNull(GraphQLBoolean), - resolve: ({isStarter}) => !!isStarter, - description: 'True if this is a starter/default scale; false otherwise' - }, - removedAt: { - type: GraphQLISO8601Type, - description: 'The datetime that the scale was removed. Null if it has not been removed.' - }, - teamId: { - type: new GraphQLNonNull(GraphQLID), - description: 'foreign key. use the team field' - }, - team: { - type: new GraphQLNonNull(Team), - description: 'The team that owns this template scale', - resolve: resolveTeam - }, - updatedAt: { - type: new GraphQLNonNull(GraphQLISO8601Type) - }, - name: { - type: new GraphQLNonNull(GraphQLString), - description: 'The title of the scale used in the template' - }, - dimensions: { - type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(TemplateDimension))), - description: 'The dimensions currently using this scale', - resolve: async ({id: scaleId, teamId}) => { - const r = await getRethink() - return r - .table('TemplateDimension') - .getAll(teamId, {index: 'teamId'}) - .filter((row: RDatum) => - row('removedAt').default(null).eq(null).and(row('scaleId').eq(scaleId)) - ) - .run() - } - }, - values: { - type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(TemplateScaleValue))), - description: 'The values used in this scale', - resolve: ({id, values}) => { - return values.map((value, index) => ({ - ...value, - scaleId: id, - sortOrder: index - })) - } - } - }) + fields: {} }) export default TemplateScale diff --git a/packages/server/graphql/types/TemplateScaleRef.ts b/packages/server/graphql/types/TemplateScaleRef.ts index 8d706c98625..78ab520da42 100644 --- a/packages/server/graphql/types/TemplateScaleRef.ts +++ b/packages/server/graphql/types/TemplateScaleRef.ts @@ -1,36 +1,8 @@ -import {GraphQLID, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString} from 'graphql' -import {GQLContext} from '../graphql' -import GraphQLISO8601Type from './GraphQLISO8601Type' -import TemplateScaleValue from './TemplateScaleValue' +import {GraphQLObjectType} from 'graphql' -const TemplateScaleRef = new GraphQLObjectType({ +const TemplateScaleRef = new GraphQLObjectType({ name: 'TemplateScaleRef', - description: 'An immutable version of TemplateScale to be shared across all users', - fields: () => ({ - id: { - type: new GraphQLNonNull(GraphQLID), - description: 'md5 hash' - }, - createdAt: { - type: new GraphQLNonNull(GraphQLISO8601Type) - }, - name: { - type: new GraphQLNonNull(GraphQLString), - description: 'The title of the scale used in the template' - }, - values: { - type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(TemplateScaleValue))), - description: 'The values used in this scale', - resolve: ({id, values}) => { - return values.map(({color, label}: {color: string; label: string}, index: number) => ({ - color, - label, - scaleId: id, - sortOrder: index - })) - } - } - }) + fields: {} }) export default TemplateScaleRef diff --git a/packages/server/graphql/types/TemplateScaleValue.ts b/packages/server/graphql/types/TemplateScaleValue.ts deleted file mode 100644 index e9dfc193629..00000000000 --- a/packages/server/graphql/types/TemplateScaleValue.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {GraphQLID, GraphQLInt, GraphQLNonNull, GraphQLObjectType, GraphQLString} from 'graphql' -import {GQLContext} from '../graphql' - -const TemplateScaleValue = new GraphQLObjectType({ - name: 'TemplateScaleValue', - description: 'A value for a scale.', - fields: () => ({ - id: { - type: new GraphQLNonNull(GraphQLID), - resolve: ({scaleId, label}) => { - return `${scaleId}:${label}` - } - }, - scaleId: { - description: 'The id of the scale this value belongs to', - type: new GraphQLNonNull(GraphQLID) - }, - color: { - description: 'The color used to visually group a scale value', - type: new GraphQLNonNull(GraphQLString) - }, - label: { - description: 'The label for this value, e.g., XS, M, L', - type: new GraphQLNonNull(GraphQLString) - }, - - sortOrder: { - type: new GraphQLNonNull(GraphQLInt), - description: 'the order of the scale value in this scale' - } - }) -}) - -export default TemplateScaleValue diff --git a/packages/server/postgres/migrations/1614030642692_refs-for-meetings.ts b/packages/server/postgres/migrations/1614030642692_refs-for-meetings.ts index 152c5c99793..b024cd537d8 100644 --- a/packages/server/postgres/migrations/1614030642692_refs-for-meetings.ts +++ b/packages/server/postgres/migrations/1614030642692_refs-for-meetings.ts @@ -8,7 +8,6 @@ import {RValue, r} from 'rethinkdb-ts' import {parse} from 'url' import MeetingPoker from '../../database/types/MeetingPoker' import TemplateDimension from '../../database/types/TemplateDimension' -import TemplateScale from '../../database/types/TemplateScale' import {insertTemplateRefQuery, insertTemplateScaleRefQuery} from '../generatedMigrationHelpers' import getPgConfig from '../getPgConfig' @@ -58,11 +57,11 @@ export async function up(): Promise { .run()) as { id: string name: string - dimensions: (TemplateDimension & {scale: TemplateScale})[] + dimensions: (TemplateDimension & {scale: any})[] }[] const uniqueScaleIdSet = new Set() - const uniqueScales = [] as TemplateScale[] + const uniqueScales = [] as any[] dimensionsByTemplateId.forEach((group) => { const {dimensions} = group dimensions.forEach((dimension) => { diff --git a/packages/server/postgres/migrations/1698265600000_addPrioritizationTemplates.ts b/packages/server/postgres/migrations/1698265600000_addPrioritizationTemplates.ts index a73875ad16c..49bf054d3bd 100644 --- a/packages/server/postgres/migrations/1698265600000_addPrioritizationTemplates.ts +++ b/packages/server/postgres/migrations/1698265600000_addPrioritizationTemplates.ts @@ -3,7 +3,6 @@ import {Client} from 'pg' import {r} from 'rethinkdb-ts' import connectRethinkDB from '../../database/connectRethinkDB' import TemplateDimension from '../../database/types/TemplateDimension' -import TemplateScale from '../../database/types/TemplateScale' import getPgConfig from '../getPgConfig' import getPgp from '../getPgp' @@ -46,7 +45,7 @@ const getTemplateIllustrationUrl = (filename: string) => { throw new Error('Mssing Env: FILE_STORE_PROVIDER') } -const MOSCOW_SCALE_CONFIG: TemplateScale = { +const MOSCOW_SCALE_CONFIG = { createdAt, id: 'moscowScale', isStarter: true, diff --git a/packages/server/postgres/migrations/1721405703862_TemplateScale.ts b/packages/server/postgres/migrations/1721405703862_TemplateScale.ts new file mode 100644 index 00000000000..7926e454beb --- /dev/null +++ b/packages/server/postgres/migrations/1721405703862_TemplateScale.ts @@ -0,0 +1,149 @@ +import {Kysely, PostgresDialect, sql} from 'kysely' +import {Client} from 'pg' +import {r} from 'rethinkdb-ts' +import connectRethinkDB from '../../database/connectRethinkDB' +import getPg from '../getPg' +import getPgConfig from '../getPgConfig' + +const START_CHAR_CODE = 32 +const END_CHAR_CODE = 126 + +export function positionAfter(pos: string) { + for (let i = pos.length - 1; i >= 0; i--) { + const curCharCode = pos.charCodeAt(i) + if (curCharCode < END_CHAR_CODE) { + return pos.substr(0, i) + String.fromCharCode(curCharCode + 1) + } + } + return pos + String.fromCharCode(START_CHAR_CODE + 1) +} + +export async function up() { + await connectRethinkDB() + const pg = new Kysely({ + dialect: new PostgresDialect({ + pool: getPg() + }) + }) + await sql` + DO $$ + BEGIN + CREATE TABLE IF NOT EXISTS "TemplateScale" ( + "id" VARCHAR(100) PRIMARY KEY, + "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + "name" VARCHAR(50) NOT NULL, + "teamId" VARCHAR(100) NOT NULL, + "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(), + "parentScaleId" VARCHAR(100), + "isStarter" BOOLEAN NOT NULL DEFAULT FALSE, + "removedAt" TIMESTAMP WITH TIME ZONE, + CONSTRAINT "fk_teamId" + FOREIGN KEY("teamId") + REFERENCES "Team"("id") + ON DELETE CASCADE + ); + CREATE INDEX IF NOT EXISTS "idx_TemplateScale_teamId" ON "TemplateScale"("teamId") WHERE "removedAt" IS NULL; + DROP TRIGGER IF EXISTS "update_TemplateScale_updatedAt" ON "TemplateScale"; + CREATE TRIGGER "update_TemplateScale_updatedAt" BEFORE UPDATE ON "TemplateScale" FOR EACH ROW EXECUTE PROCEDURE "set_updatedAt"(); + + CREATE TABLE IF NOT EXISTS "TemplateScaleValue" ( + "id" INT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + "templateScaleId" VARCHAR(100) NOT NULL, + "sortOrder" VARCHAR(64) NOT NULL COLLATE "C", + "color" VARCHAR(9) NOT NULL, + "label" VARCHAR(18) NOT NULL, + UNIQUE ("templateScaleId","label"), + CONSTRAINT "fk_templateScaleId" + FOREIGN KEY("templateScaleId") + REFERENCES "TemplateScale"("id") + ON DELETE CASCADE + ); + CREATE INDEX IF NOT EXISTS "idx_TemplateScaleValue_templateScaleId" ON "TemplateScaleValue"("templateScaleId"); + CREATE OR REPLACE FUNCTION "set_TemplateScale_updatedAt"() + RETURNS TRIGGER AS $t$ + BEGIN + -- Update the updatedAt column in TemplateScale + UPDATE "TemplateScale" + SET "updatedAt" = CURRENT_TIMESTAMP + WHERE id = NEW."templateScaleId"; + RETURN NEW; + END; + $t$ LANGUAGE plpgsql; + CREATE TRIGGER "update_TemplateScale_updatedAt_from_TemplateScaleValue" + AFTER INSERT OR UPDATE OR DELETE ON "TemplateScaleValue" + FOR EACH ROW + EXECUTE FUNCTION "set_TemplateScale_updatedAt"(); + END $$; +`.execute(pg) + + const rTemplateScales = await r.table('TemplateScale').coerceTo('array').run() + const templateScaleValues = [] as { + templateScaleId: string + sortOrder: string + color: string + label: string + }[] + const templateScales = rTemplateScales.map((templateScale) => { + const {id, createdAt, name, teamId, updatedAt, parentScaleId, isStarter, removedAt, values} = + templateScale + let curSortOrder = '' + const templateVals = values?.map((value) => { + const sortOrder = positionAfter(curSortOrder) + curSortOrder = sortOrder + return { + templateScaleId: id, + sortOrder, + color: value.color, + label: value.label.slice(0, 8) + } + }) + templateScaleValues.push(...templateVals) + return {id, createdAt, name, teamId, updatedAt, parentScaleId, isStarter, removedAt} + }) + + const chunk = (arr: any[], size: number) => + Array.from({length: Math.ceil(arr.length / size)}, (_, i) => + arr.slice(i * size, i * size + size) + ) + + const valueChunks = chunk(templateScaleValues, 10000) + + await pg.insertInto('TemplateScale').values(templateScales).execute() + await Promise.all( + valueChunks.map(async (chunk) => { + try { + return await pg.insertInto('TemplateScaleValue').values(chunk).execute() + } catch (e) { + await Promise.all( + chunk.map(async (row) => { + try { + await pg + .insertInto('TemplateScaleValue') + .values(row) + .onConflict((oc) => oc.doNothing()) + .execute() + } catch (e) { + console.log(e, row) + } + }) + ) + } + }) + ) + + await pg.schema + .alterTable('TemplateScale') + .addForeignKeyConstraint('fk_parentScaleId', ['parentScaleId'], 'TemplateScale', ['id']) + .onDelete('set null') + .execute() +} + +export async function down() { + const client = new Client(getPgConfig()) + await client.connect() + await client.query(` + DROP TABLE IF EXISTS "TemplateScaleValue"; + DROP TABLE IF EXISTS "TemplateScale"; + ` /* Do undo magic */) + await client.end() +} diff --git a/packages/server/postgres/queries/src/insertTemplateRefQuery.sql b/packages/server/postgres/queries/src/insertTemplateRefQuery.sql deleted file mode 100644 index f0da3dc65ca..00000000000 --- a/packages/server/postgres/queries/src/insertTemplateRefQuery.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - @name insertTemplateRefQuery - @param ref -> (id, template) -*/ -INSERT INTO "TemplateRef" ( - "id", - "template" -) -VALUES :ref -ON CONFLICT (id) -DO NOTHING; diff --git a/packages/server/postgres/queries/src/insertTemplateScaleRefQuery.sql b/packages/server/postgres/queries/src/insertTemplateScaleRefQuery.sql deleted file mode 100644 index 68e8aca0ab5..00000000000 --- a/packages/server/postgres/queries/src/insertTemplateScaleRefQuery.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - @name insertTemplateScaleRefQuery - @param templateScales -> ((id, scale)...) -*/ -INSERT INTO "TemplateScaleRef" ( - "id", - "scale" -) -VALUES :templateScales -ON CONFLICT (id) -DO NOTHING; diff --git a/packages/server/postgres/select.ts b/packages/server/postgres/select.ts new file mode 100644 index 00000000000..59af2b66533 --- /dev/null +++ b/packages/server/postgres/select.ts @@ -0,0 +1,18 @@ +import {sql} from 'kysely' +import getKysely from './getKysely' + +export const selectTemplateScale = () => { + return getKysely() + .selectFrom('TemplateScale') + .where('removedAt', 'is', null) + .leftJoin('TemplateScaleValue', 'TemplateScale.id', 'TemplateScaleValue.templateScaleId') + .groupBy('TemplateScale.id') + .selectAll('TemplateScale') + .select(() => [ + sql< + {color: string; label: string; sortOrder: string}[] + >`json_agg(json_build_object('color', "TemplateScaleValue".color, 'label', "TemplateScaleValue".label, 'sortOrder', "TemplateScaleValue"."sortOrder") ORDER BY "TemplateScaleValue"."sortOrder")`.as( + 'values' + ) + ]) +} diff --git a/packages/server/postgres/types/index.d.ts b/packages/server/postgres/types/index.d.ts index 558bc238192..ea5fcd37de1 100644 --- a/packages/server/postgres/types/index.d.ts +++ b/packages/server/postgres/types/index.d.ts @@ -1,6 +1,22 @@ -import {Selectable} from 'kysely' -import {OrganizationUser as OrganizationUserPG, TeamMember as TeamMemberPG} from '../pg.d' +import {SelectQueryBuilder, Selectable} from 'kysely' +import { + OrganizationUser as OrganizationUserPG, + TeamMember as TeamMemberPG, + TemplateScaleRef as TemplateScaleRefPG +} from '../pg.d' +import {selectTemplateScale} from '../select' + +type ExtractTypeFromQueryBuilderSelect any> = + ReturnType extends SelectQueryBuilder ? X : never export type OrganizationUser = Selectable export type TeamMember = Selectable + +export type TemplateScale = ExtractTypeFromQueryBuilderSelect + +// TODO refactor getTemplateScaleRefsByIds to kysely +export type TemplateScaleRef = Selectable & { + name: string + values: {color: string; label: string}[] +} diff --git a/packages/server/utils/analytics/analytics.ts b/packages/server/utils/analytics/analytics.ts index 741e73ab6ab..61f672d9931 100644 --- a/packages/server/utils/analytics/analytics.ts +++ b/packages/server/utils/analytics/analytics.ts @@ -8,12 +8,12 @@ import MeetingTemplate from '../../database/types/MeetingTemplate' import {Reactable, ReactableEnum} from '../../database/types/Reactable' import {SlackNotificationEventEnum} from '../../database/types/SlackNotification' import {TaskServiceEnum} from '../../database/types/Task' -import TemplateScale from '../../database/types/TemplateScale' import {DataLoaderWorker} from '../../graphql/graphql' import {ModifyType} from '../../graphql/public/resolverTypes' import {IntegrationProviderServiceEnumType} from '../../graphql/types/IntegrationProviderServiceEnum' import {UpgradeCTALocationEnumType} from '../../graphql/types/UpgradeCTALocationEnum' import {TeamPromptResponse} from '../../postgres/queries/getTeamPromptResponsesByIds' +import {TemplateScale} from '../../postgres/types' import {MeetingTypeEnum} from '../../postgres/types/Meeting' import {MeetingSeries} from '../../postgres/types/MeetingSeries' import {AmplitudeAnalytics} from './amplitude/AmplitudeAnalytics' @@ -413,7 +413,7 @@ class Analytics { scaleMetrics = ( user: AnalyticsUser, - scale: TemplateScale, + scale: Pick, eventName: 'Scale Created' | 'Scale Cloned' ) => { this.track(user, eventName, { diff --git a/packages/server/utils/sortOrder.ts b/packages/server/utils/sortOrder.ts new file mode 100644 index 00000000000..aa03d336ded --- /dev/null +++ b/packages/server/utils/sortOrder.ts @@ -0,0 +1,67 @@ +// https://www.figma.com/blog/realtime-editing-of-ordered-sequences/#fractional-indexing/ +// https://steve.dignam.xyz/2020/03/31/practical-ordering/ +// Creates a sortOrder that is a string of characters that can be compared lexicographically +// This is beneficial because each change results in 1 update to the DB & there is no rebalancing necessary +// WARNING: the lexicographical sort assumes a C collation (i.e. compare strings via ASCII code) +// CloudSQL assumes a utf-8 (byte-code) comparision by default, so make sure the column is collated correctly! + +const START_CHAR_CODE = 32 +const END_CHAR_CODE = 126 + +export function positionBefore(pos: string) { + for (let i = pos.length - 1; i >= 0; i--) { + const curCharCode = pos.charCodeAt(i) + if (curCharCode > START_CHAR_CODE + 1) { + return pos.substr(0, i) + String.fromCharCode(curCharCode - 1) + } + } + return ( + pos.substr(0, pos.length - 1) + + String.fromCharCode(START_CHAR_CODE) + + String.fromCharCode(END_CHAR_CODE) + ) +} +export function positionAfter(pos: string) { + for (let i = pos.length - 1; i >= 0; i--) { + const curCharCode = pos.charCodeAt(i) + if (curCharCode < END_CHAR_CODE) { + return pos.substr(0, i) + String.fromCharCode(curCharCode + 1) + } + } + return pos + String.fromCharCode(START_CHAR_CODE + 1) +} + +function avg(a: number, b: number) { + return Math.trunc((a + b) / 2) +} + +function positionBetween(firstPos: string, secondPos: string) { + let flag = false + let position = '' + const maxLength = Math.max(firstPos.length, secondPos.length) + for (let i = 0; i < maxLength; i++) { + const lower = i < firstPos.length ? firstPos.charCodeAt(i) : START_CHAR_CODE + const upper = i < secondPos.length && !flag ? secondPos.charCodeAt(i) : END_CHAR_CODE + if (lower === upper) { + position += String.fromCharCode(lower) + } else if (upper - lower > 1) { + position += String.fromCharCode(avg(lower, upper)) + flag = false + break + } else { + position += String.fromCharCode(lower) + flag = true + } + } + if (!flag) return position + return position + String.fromCharCode(avg(START_CHAR_CODE, END_CHAR_CODE)) +} + +export function getSortOrder(arr: {sortOrder: string}[], fromIdx: number, toIdx: number) { + const secondPosIdx = fromIdx < toIdx ? toIdx + 1 : toIdx + const firstPos = arr[secondPosIdx - 1]?.sortOrder + const secondPos = arr[secondPosIdx]?.sortOrder + if (firstPos && secondPos) return positionBetween(firstPos, secondPos) + if (secondPos) return positionBefore(secondPos) + return positionAfter(firstPos || '') +} From 88a3539e6f75a140e2ba4e1e15b23d71c865f945 Mon Sep 17 00:00:00 2001 From: "parabol-release-bot[bot]" <150284312+parabol-release-bot[bot]@users.noreply.github.com> Date: Wed, 24 Jul 2024 17:21:59 -0700 Subject: [PATCH 08/18] chore(release): release v7.39.3 (#10031) Co-authored-by: parabol-release-bot[bot] <150284312+parabol-release-bot[bot]@users.noreply.github.com> --- .release-please-manifest.json | 2 +- CHANGELOG.md | 7 +++++++ package.json | 2 +- packages/chronos/package.json | 4 ++-- packages/client/package.json | 2 +- packages/embedder/package.json | 2 +- packages/gql-executor/package.json | 6 +++--- packages/integration-tests/package.json | 2 +- packages/server/package.json | 4 ++-- 9 files changed, 19 insertions(+), 12 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a999d60cc0b..2f8093d94f6 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "7.39.2" + ".": "7.39.3" } diff --git a/CHANGELOG.md b/CHANGELOG.md index 09b9fa1731c..6654a361cd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ This project adheres to [Semantic Versioning](http://semver.org/). This CHANGELOG follows conventions [outlined here](http://keepachangelog.com/). +## [7.39.3](https://github.com/ParabolInc/parabol/compare/v7.39.2...v7.39.3) (2024-07-25) + + +### Changed + +* **rethinkdb:** TemplateScale: One-shot ([#10021](https://github.com/ParabolInc/parabol/issues/10021)) ([0c6c8e7](https://github.com/ParabolInc/parabol/commit/0c6c8e79dfbbac93362b3db640b008e32ba701b3)) + ## [7.39.2](https://github.com/ParabolInc/parabol/compare/v7.39.1...v7.39.2) (2024-07-24) diff --git a/package.json b/package.json index 7351040d65d..94dd7ffa721 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.2", + "version": "7.39.3", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/chronos/package.json b/packages/chronos/package.json index 8852d449624..a48ced587a5 100644 --- a/packages/chronos/package.json +++ b/packages/chronos/package.json @@ -1,6 +1,6 @@ { "name": "chronos", - "version": "7.39.2", + "version": "7.39.3", "description": "A cron job scheduler", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/chronos#readme", @@ -25,6 +25,6 @@ }, "dependencies": { "cron": "^2.3.1", - "parabol-server": "7.39.2" + "parabol-server": "7.39.3" } } diff --git a/packages/client/package.json b/packages/client/package.json index 5ece805bce9..00da4eecb33 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.2", + "version": "7.39.3", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" diff --git a/packages/embedder/package.json b/packages/embedder/package.json index e78210071f3..a5258421eac 100644 --- a/packages/embedder/package.json +++ b/packages/embedder/package.json @@ -1,6 +1,6 @@ { "name": "parabol-embedder", - "version": "7.39.2", + "version": "7.39.3", "description": "A service that computes embedding vectors from Parabol objects", "author": "Jordan Husney ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/embedder#readme", diff --git a/packages/gql-executor/package.json b/packages/gql-executor/package.json index 0738c1b2524..1623ef6af34 100644 --- a/packages/gql-executor/package.json +++ b/packages/gql-executor/package.json @@ -1,6 +1,6 @@ { "name": "gql-executor", - "version": "7.39.2", + "version": "7.39.3", "description": "A Stateless GraphQL Executor", "author": "Matt Krick ", "homepage": "https://github.com/ParabolInc/parabol/tree/master/packages/gqlExecutor#readme", @@ -27,8 +27,8 @@ }, "dependencies": { "dd-trace": "^4.2.0", - "parabol-client": "7.39.2", - "parabol-server": "7.39.2", + "parabol-client": "7.39.3", + "parabol-server": "7.39.3", "undici": "^5.26.2" } } diff --git a/packages/integration-tests/package.json b/packages/integration-tests/package.json index 2626544317a..95a50e4e3af 100644 --- a/packages/integration-tests/package.json +++ b/packages/integration-tests/package.json @@ -2,7 +2,7 @@ "name": "integration-tests", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.2", + "version": "7.39.3", "description": "", "main": "index.js", "scripts": { diff --git a/packages/server/package.json b/packages/server/package.json index e8ed5217f59..f9156cd8334 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -3,7 +3,7 @@ "description": "An open-source app for building smarter, more agile teams.", "author": "Parabol Inc. (http://github.com/ParabolInc)", "license": "AGPL-3.0", - "version": "7.39.2", + "version": "7.39.3", "repository": { "type": "git", "url": "https://github.com/ParabolInc/parabol" @@ -124,7 +124,7 @@ "openai": "^4.24.1", "openapi-fetch": "^0.9.7", "oy-vey": "^0.12.1", - "parabol-client": "7.39.2", + "parabol-client": "7.39.3", "pg": "^8.5.1", "react": "^17.0.2", "react-dom": "^17.0.2", From fa52c4638629fcf9c5c3453798d1320b9d8da2dd Mon Sep 17 00:00:00 2001 From: Georg Bremer Date: Thu, 25 Jul 2024 12:46:37 +0200 Subject: [PATCH 09/18] fix: Disable SAML on downgrade to starter (#10026) --- .../graphql/mutations/helpers/resolveDowngradeToStarter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/graphql/mutations/helpers/resolveDowngradeToStarter.ts b/packages/server/graphql/mutations/helpers/resolveDowngradeToStarter.ts index 8e44110824c..86d5fcc0a4b 100644 --- a/packages/server/graphql/mutations/helpers/resolveDowngradeToStarter.ts +++ b/packages/server/graphql/mutations/helpers/resolveDowngradeToStarter.ts @@ -38,7 +38,7 @@ const resolveDowngradeToStarter = async ( .execute(), pg .updateTable('SAML') - .set({metadata: null, lastUpdatedBy: user.id}) + .set({metadata: null, url: null, lastUpdatedBy: user.id}) .where('orgId', '=', orgId) .execute(), updateTeamByOrgId( From 2dd490ac75578f73d7a7b3e1f8ccfb1cd2164884 Mon Sep 17 00:00:00 2001 From: Terry Acker Date: Thu, 25 Jul 2024 10:36:38 -0500 Subject: [PATCH 10/18] feat: nav updates (#9973) --- .../components/DashNavList/DashNavList.tsx | 97 ++++++++---------- .../DashNavList/DashNavListTeams.tsx | 6 +- .../components/DashNavList/DashNavMenu.tsx | 98 ------------------- .../components/Dashboard/DashSidebar.tsx | 19 ++-- .../components/Dashboard/LeftDashNavItem.tsx | 10 +- .../Dashboard/MobileDashSidebar.tsx | 27 +++-- .../components/SideBarStartMeetingButton.tsx | 4 +- .../components/Organization/OrgNav.tsx | 18 ++-- 8 files changed, 83 insertions(+), 196 deletions(-) delete mode 100644 packages/client/components/DashNavList/DashNavMenu.tsx diff --git a/packages/client/components/DashNavList/DashNavList.tsx b/packages/client/components/DashNavList/DashNavList.tsx index 33a61b06965..bda7d56a650 100644 --- a/packages/client/components/DashNavList/DashNavList.tsx +++ b/packages/client/components/DashNavList/DashNavList.tsx @@ -1,17 +1,14 @@ import styled from '@emotion/styled' +import {ManageAccounts} from '@mui/icons-material' import graphql from 'babel-plugin-relay/macro' import React from 'react' import {useFragment} from 'react-relay' -import {PALETTE} from '~/styles/paletteV3' import {DashNavList_organization$key} from '../../__generated__/DashNavList_organization.graphql' -import {TierEnum} from '../../__generated__/InvoiceHeader_invoice.graphql' -import useBreakpoint from '../../hooks/useBreakpoint' -import {Breakpoint} from '../../types/constEnums' -import {upperFirst} from '../../utils/upperFirst' -import LeftDashNavItem from '../Dashboard/LeftDashNavItem' -import BaseTag from '../Tag/BaseTag' +import {TierEnum} from '../../__generated__/OrganizationSubscription.graphql' +import {Tooltip} from '../../ui/Tooltip/Tooltip' +import {TooltipContent} from '../../ui/Tooltip/TooltipContent' +import {TooltipTrigger} from '../../ui/Tooltip/TooltipTrigger' import DashNavListTeams from './DashNavListTeams' -import DashNavMenu from './DashNavMenu' const EmptyTeams = styled('div')({ fontSize: 16, @@ -20,22 +17,13 @@ const EmptyTeams = styled('div')({ textAlign: 'center' }) -const StyledLeftDashNavItem = styled(LeftDashNavItem)<{isViewerOnTeam: boolean}>( - ({isViewerOnTeam}) => ({ - color: isViewerOnTeam ? PALETTE.SLATE_700 : PALETTE.SLATE_600, - borderRadius: 44, - paddingLeft: 15 - }) -) - -const Tag = styled(BaseTag)<{tier: TierEnum | null}>(({tier}) => ({ - backgroundColor: - tier === 'enterprise' ? PALETTE.SKY_500 : tier === 'team' ? PALETTE.GOLD_300 : PALETTE.JADE_400, - color: tier === 'team' ? PALETTE.GRAPE_700 : PALETTE.WHITE -})) +const StyledIcon = styled(ManageAccounts)({ + height: 18, + width: 18 +}) interface Props { - organizationsRef: DashNavList_organization$key | null + organizationsRef: DashNavList_organization$key onClick?: () => void } @@ -45,7 +33,6 @@ const DashNavList = (props: Props) => { graphql` fragment DashNavList_organization on Organization @relay(plural: true) { ...DashNavListTeams_organization - ...DashNavMenu_organization id name tier @@ -56,44 +43,42 @@ const DashNavList = (props: Props) => { `, organizationsRef ) - const isDesktop = useBreakpoint(Breakpoint.SIDEBAR_LEFT) - const teams = organizations?.flatMap((org) => org.viewerTeams) + + const TierEnumValues: TierEnum[] = ['enterprise', 'team', 'starter'] + + const sortedOrgs = organizations.toSorted((a, b) => { + const aTier = TierEnumValues.indexOf(a.tier) + const bTier = TierEnumValues.indexOf(b.tier) + return aTier < bTier ? -1 : aTier > bTier ? 1 : a.name.localeCompare(b.name) + }) + + const teams = organizations.flatMap((org) => org.viewerTeams) if (teams?.length === 0) { - return It appears you are not a member of any team! + return {'It appears you are not a member of any team!'} } return ( -
- {organizations?.map((org) => ( -
-
0 ? `border-b border-solid border-slate-300 p-2` : 'p-2' - } - > -
-
- - {org.name} - -
- {upperFirst(org.tier)} -
-
-
- {isDesktop ? ( - - ) : ( - - )} +
+ {sortedOrgs.map((org) => ( +
+
+ + {org.name} + + + + + + + + + {'Settings & Members'} + +
diff --git a/packages/client/components/DashNavList/DashNavListTeams.tsx b/packages/client/components/DashNavList/DashNavListTeams.tsx index 3c9e22099bb..560567cdb79 100644 --- a/packages/client/components/DashNavList/DashNavListTeams.tsx +++ b/packages/client/components/DashNavList/DashNavListTeams.tsx @@ -12,7 +12,7 @@ const StyledLeftDashNavItem = styled(LeftDashNavItem)<{isPublicTeams?: boolean}> ({isPublicTeams}) => ({ color: isPublicTeams ? PALETTE.SLATE_600 : PALETTE.SLATE_700, borderRadius: 44, - paddingLeft: 15 + paddingLeft: 16 }) ) @@ -61,7 +61,7 @@ const DashNavListTeams = (props: Props) => { if (!viewerTeams.length) return null return ( -
+
{viewerTeams.map((team) => { return ( { })} {publicTeamsCount > 0 && ( ( - ({isViewerOnTeam}) => ({ - color: isViewerOnTeam ? PALETTE.SLATE_700 : PALETTE.SLATE_600, - borderRadius: 44, - paddingLeft: 15 - }) -) - -type Props = { - organizationRef: DashNavMenu_organization$key -} - -const DashNavMenu = (props: Props) => { - const {organizationRef} = props - const history = useHistory() - const org = useFragment( - graphql` - fragment DashNavMenu_organization on Organization { - id - tier - } - `, - organizationRef - ) - const {id: orgId, tier} = org - const menuItems = [ - { - label: ( - <> - Plans & Billing{' '} - {tier === 'starter' && ( - <> - • Upgrade - - )} - - ), - href: `/me/organizations/${orgId}/billing` - }, - { - label: 'Teams', - href: `/me/organizations/${orgId}/teams` - }, - { - label: 'Members', - href: `/me/organizations/${orgId}/members` - }, - { - label: 'Organization Settings', - href: `/me/organizations/${orgId}/settings` - }, - { - label: 'Authentication', - href: `/me/organizations/${orgId}/authentication` - } - ] - - const handleMenuItemClick = (href: string) => { - history.push(href) - } - - return ( - - -
- } - > - - {menuItems.map((item) => ( - handleMenuItemClick(item.href)}> - {item.label} - - ))} - - - ) -} - -export default DashNavMenu diff --git a/packages/client/components/Dashboard/DashSidebar.tsx b/packages/client/components/Dashboard/DashSidebar.tsx index e919517f9e4..fb7ee5afdd9 100644 --- a/packages/client/components/Dashboard/DashSidebar.tsx +++ b/packages/client/components/Dashboard/DashSidebar.tsx @@ -4,7 +4,6 @@ import React from 'react' import {useFragment} from 'react-relay' import {useRouteMatch} from 'react-router' import {DashSidebar_viewer$key} from '../../__generated__/DashSidebar_viewer.graphql' -import {PALETTE} from '../../styles/paletteV3' import {NavSidebar} from '../../types/constEnums' import { AUTHENTICATION_PAGE, @@ -40,7 +39,7 @@ const NavMain = styled('div')({ const NavItem = styled(LeftDashNavItem)({ borderRadius: 44, - paddingLeft: 15 + paddingLeft: 16 }) const NavList = styled(DashNavList)({ @@ -56,14 +55,6 @@ const Wrapper = styled('div')({ flexDirection: 'column' }) -const OrgName = styled('div')({ - color: PALETTE.SLATE_600, - fontWeight: 600, - fontSize: 12, - lineHeight: '16px', - padding: '8px 16px' -}) - interface Props { isOpen: boolean viewerRef: DashSidebar_viewer$key | null @@ -107,7 +98,11 @@ const DashSidebar = (props: Props) => { label={'Organizations'} exact /> - {name} +
+ + {name} + +
{