Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(rethinkdb): SuggestedAction: Phase 3 #10043

Merged
merged 19 commits into from
Aug 5, 2024

Conversation

mattkrick
Copy link
Member

@mattkrick mattkrick commented Jul 26, 2024

Description

This one got ugly because I had to get SuggestedAction out of the graphql/User object, so I ended up just migrating User to the SDL pattern.

Summary by CodeRabbit

  • New Features

    • Introduced a new type for suggested actions in the schema.
    • Added new asynchronous resolver methods for enhanced user data retrieval.
  • Improvements

    • Enhanced type definitions for better consistency across the application.
    • Updated logic for GraphQL queries, improving clarity and data retrieval.
    • Streamlined control flow for user-related data interactions.
    • Optimised performance of demo entity retrieval with cached lookups.
  • Bug Fixes

    • Resolved issues with data retrieval by standardising naming conventions in GraphQL queries.
  • Chores

    • Transitioned database interactions from RethinkDB to PostgreSQL for several functions.

@github-actions github-actions bot requested a review from tianrunhe July 26, 2024 22:32
Copy link
Contributor

coderabbitai bot commented Jul 26, 2024

Walkthrough

The recent changes involve a significant shift from RethinkDB to PostgreSQL, enhancing various functionalities such as user bootstrap processes and suggested actions. Updates to type definitions, GraphQL queries, and resolver types improve consistency and type safety. Overall, these modifications streamline data access and manipulation across the application, leading to greater architectural efficiency and maintainability.

Changes

File Path Change Summary
codegen.json Duplicate entry for CreateGcalEventInput removed; added SuggestedAction type as SuggestedActionDB.
packages/client/modules/email/components/* Renamed newMeeting to meeting across multiple components, updating data access accordingly.
packages/server/dataloader/* Renamed constants related to suggested actions for clarity; updated loader interfaces.
packages/server/graphql/mutations/helpers/bootstrapNewUser.ts Transitioned from RethinkDB to PostgreSQL for user bootstrap logic, streamlining action creation and database interactions.
packages/server/graphql/rootTypes.ts Removed several suggested action types from imports and the rootTypes array, indicating a refactor of related features.
packages/server/postgres/select.ts Enhanced the selectSuggestedAction function for improved type safety and active suggestion retrieval.
packages/server/postgres/types/index.d.ts Introduced new SuggestedAction type derived from the selectSuggestedAction return type.
packages/server/graphql/public/types/User.ts Added new resolvers for user interactions and improved existing ones for better data retrieval.
packages/server/postgres/migrations/1722527208553_SuggestAction-phase2.ts New migration script for managing the SuggestedAction table, facilitating data migration from RethinkDB to PostgreSQL.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Frontend
    participant GraphQL
    participant PostgreSQL

    User->>Frontend: Request action (e.g., sign up)
    Frontend->>GraphQL: Call signUpWithPassword
    GraphQL->>PostgreSQL: Insert user data
    PostgreSQL-->>GraphQL: Confirmation
    GraphQL-->>Frontend: Success response
    Frontend-->>User: Display confirmation message
Loading
sequenceDiagram
    participant User
    participant Frontend
    participant GraphQL
    participant PostgreSQL

    User->>Frontend: Request email verification
    Frontend->>GraphQL: Call verifyEmail
    GraphQL->>PostgreSQL: Check verification token
    PostgreSQL-->>GraphQL: Confirmation
    GraphQL-->>Frontend: Verification success
    Frontend-->>User: Notify verification success
Loading

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

Share
Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (invoked as PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Outside diff range, codebase verification and nitpick comments (5)
packages/server/graphql/types/User.ts (1)

3-5: Empty User type.

The User type has been reduced to an empty object. This change will likely break many existing queries and mutations that rely on user data. Please verify that this change is intentional and part of a broader refactor.

  • Location of concern:
    • packages/server/graphql/types/User.ts
Analysis chain

Empty User type.

The User type has been reduced to an empty object. Ensure that this change aligns with the overall refactor and does not break any existing queries or mutations relying on user data.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that the removal of fields from the `User` type does not break any existing queries or mutations.

# Test: Search for the usage of `User` type in the codebase. Expect: No broken queries or mutations.
rg --type graphql -A 5 $'User'

Length of output: 61


Script:

#!/bin/bash
# Description: Verify that the removal of fields from the `User` type does not break any existing queries or mutations.

# Find all GraphQL files
fd -e graphql -e gql -e graphqls > graphql_files.txt

# Search for the usage of `User` type in the found GraphQL files
if [ -s graphql_files.txt ]; then
  rg 'User' $(cat graphql_files.txt)
else
  echo "No GraphQL files found."
fi

Length of output: 129703

packages/server/dataloader/rethinkPrimaryKeyLoaderMakers.ts (1)

Verify removal of suggestedActions.

The suggestedActions is still referenced in multiple places in the codebase. Its removal from the export in rethinkPrimaryKeyLoaderMakers.ts might break dependent code. Please review the following files for potential issues:

  • packages/server/safeMutations/acceptTeamInvitation.ts
  • packages/server/graphql/public/typeDefs/User.graphql
  • packages/server/graphql/mutations/dismissSuggestedAction.ts
  • packages/server/graphql/public/types/User.ts
  • packages/server/dataloader/primaryKeyLoaderMakers.ts
  • packages/server/dataloader/foreignKeyLoaderMakers.ts
  • packages/client/mutations/handlers/handleRemoveSuggestedActions.ts
  • packages/client/mutations/AcceptTeamInvitationMutation.ts
  • packages/client/components/TimelineSuggestedAction.tsx
Analysis chain

Line range hint 1-1:
Verify removal of suggestedActions.

Ensure that the removal of suggestedActions does not break any dependent code.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify that `suggestedActions` is not referenced anywhere in the codebase.

# Test: Search for references to `suggestedActions`. Expect: No occurrences found.
rg --type python -A 5 $'suggestedActions'

Length of output: 71


Script:

#!/bin/bash
# Description: Verify that `suggestedActions` is not referenced anywhere in the codebase.

# Test: Search for references to `suggestedActions`. Expect: No occurrences found.
rg 'suggestedActions'

Length of output: 1822

packages/server/dataloader/rethinkForeignKeyLoaderMakers.ts (1)

Issue with removal of suggestedActionsByUserId

The export suggestedActionsByUserId is still being referenced in multiple files, which will cause runtime errors if it is removed.

  • File: packages/server/safeMutations/acceptTeamInvitation.ts
    • Line: const suggestedActions = await dataLoader.get('suggestedActionsByUserId').load(userId)
  • File: packages/server/dataloader/foreignKeyLoaderMakers.ts
    • Line: export const suggestedActionsByUserId = foreignKeyLoaderMaker(
  • File: packages/server/graphql/public/types/User.ts
    • Line: const suggestedActions = await dataLoader.get('suggestedActionsByUserId').load(userId)

Please ensure that all references to suggestedActionsByUserId are removed or replaced appropriately before removing the export.

Analysis chain

Line range hint 1-1:
Verify the removal of suggestedActionsByUserId.

Ensure that all references to suggestedActionsByUserId are removed from the codebase to prevent any potential issues.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify all references to `suggestedActionsByUserId` are removed from the codebase.

# Test: Search for references to `suggestedActionsByUserId`. Expect: No occurrences.
rg --type js --type ts 'suggestedActionsByUserId'

Length of output: 444


Script:

#!/bin/bash
# Extract relevant lines from the files where `suggestedActionsByUserId` is referenced.

# Extract lines around the reference in acceptTeamInvitation.ts
rg --type js --type ts -A 5 -B 5 'suggestedActionsByUserId' packages/server/safeMutations/acceptTeamInvitation.ts

# Extract lines around the reference in foreignKeyLoaderMakers.ts
rg --type js --type ts -A 5 -B 5 'suggestedActionsByUserId' packages/server/dataloader/foreignKeyLoaderMakers.ts

# Extract lines around the reference in User.ts
rg --type js --type ts -A 5 -B 5 'suggestedActionsByUserId' packages/server/graphql/public/types/User.ts

Length of output: 1594

packages/server/graphql/public/typeDefs/User.graphql (1)

282-282: Issues found with changing the userIds field's type definition.

The stricter requirement for the userIds input (changing from [ID] to [ID!]) will cause issues in the following parts of the codebase where userIds can be null or where null checks are performed:

  • packages/server/graphql/public/types/User.ts
  • packages/server/graphql/private/types/FlagOverLimitPayload.ts
  • packages/server/database/migrations/20200202171128-notificationsV2.ts
  • packages/client/utils/constructFilterQueryParamURL.ts
  • packages/client/utils/useQueryParameterParser.ts
  • packages/client/mutations/connections/getUserTasksConn.ts
  • packages/client/mutations/connections/getArchivedTasksConn.ts
  • packages/client/components/ArchiveTaskUserRoot.tsx
  • packages/client/components/ArchiveTaskRoot.tsx

A thorough review and refactoring are necessary to handle the stricter type requirement for userIds.

Analysis chain

Verify the impact of changing the userIds field's type definition.

Ensure that the stricter requirement for the userIds input (changing from [ID] to [ID!]) does not cause issues in the codebase.

Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Verify the impact of changing the `userIds` field's type definition.

# Test: Search for the usage of `userIds` in the codebase. Expect: No occurrences of null values in the array.
rg --type graphql -A 5 $'userIds'

Length of output: 64


Script:

#!/bin/bash
# Description: Verify the impact of changing the `userIds` field's type definition.

# Test: Search for the usage of `userIds` in the codebase. Expect: No occurrences of null values in the array.
rg -A 5 'userIds'

Length of output: 98860


Script:

#!/bin/bash
# Description: Verify the impact of changing the `userIds` field's type definition.

# Test: Search for potential null checks or handling of `userIds` being null in the codebase.
rg -A 5 'userIds.*null'

Length of output: 5775

packages/server/graphql/public/types/User.ts (1)

123-123: Consider using optional chaining.

Change to an optional chain to simplify the code and handle potential null or undefined values safely.

- startCursor: firstEdge && firstEdge.cursor,
+ startCursor: firstEdge?.cursor,
Tools
Biome

[error] 123-123: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Comment on lines 24 to 55
const suggestedActions = await dataLoader.get('suggestedActionsByUserId').load(userId)
const hasTryRetro = suggestedActions.some((sa) => sa.type === 'tryRetroMeeting')
if (hasTryRetro) return null
const actions = [
{
id: generateUID(),
createdAt: now,
priority: 3,
removedAt: null,
teamId,
type: 'tryRetroMeeting' as const,
userId
},
{
id: generateUID(),
createdAt: now,
priority: 4,
removedAt: null,
type: 'createNewTeam' as const,
userId
},
{
id: generateUID(),
createdAt: now,
priority: 5,
removedAt: null,
teamId,
type: 'tryActionMeeting' as const,
userId
}
]
await getKysely().insertInto('SuggestedAction').values(actions).execute()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Improved logic and efficiency.

The changes to use a data loader and construct an array of action objects for insertion improve the efficiency and maintainability of the function.

Consider extracting the action object construction into a separate helper function for better readability.

const createAction = (type: string, priority: number, teamId: string, userId: string, now: Date) => ({
  id: generateUID(),
  createdAt: now,
  priority,
  removedAt: null,
  teamId,
  type,
  userId
});

const actions = [
  createAction('tryRetroMeeting', 3, teamId, userId, now),
  createAction('createNewTeam', 4, teamId, userId, now),
  createAction('tryActionMeeting', 5, teamId, userId, now)
];

@@ -77,6 +77,7 @@
"Comment": "../../database/types/Comment#default as CommentDB",
"Company": "./types/Company#CompanySource",
"CreateGcalEventInput": "./types/CreateGcalEventInput#default",
"CreateGcalEventInput": "./types/CreateGcalEventInput#default",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove duplicate entry for CreateGcalEventInput.

The entry for CreateGcalEventInput is duplicated, which can cause confusion and potential errors in the code generation process.

- "CreateGcalEventInput": "./types/CreateGcalEventInput#default",

Committable suggestion was skipped due to low confidence.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Outside diff range, codebase verification and nitpick comments (2)
packages/server/graphql/public/types/User.ts (2)

4-4: Consider removing unused import.

The MeetingMemberId import is not used in the file.

-import MeetingMemberId from 'parabol-client/shared/gqlIds/MeetingMemberId'

46-46: Consider renaming the import for clarity.

The ReqResolvers import should be renamed for clarity, as it is used to define the User resolver.

-import {ReqResolvers} from './ReqResolvers'
+import {ReqResolvers as UserResolvers} from './ReqResolvers'

return {
edges,
pageInfo: {
startCursor: firstEdge && firstEdge.cursor,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use optional chaining for safety.

Change to an optional chain to avoid potential runtime errors.

-        startCursor: firstEdge && firstEdge.cursor,
+        startCursor: firstEdge?.cursor,
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
startCursor: firstEdge && firstEdge.cursor,
startCursor: firstEdge?.cursor,
Tools
Biome

[error] 197-197: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

return {
edges,
pageInfo: {
startCursor: firstEdge && firstEdge.cursor,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use optional chaining for safety.

Change to an optional chain to avoid potential runtime errors.

-        endCursor: firstEdge && edges[edges.length - 1]!.cursor,
+        endCursor: firstEdge?.cursor,

Committable suggestion was skipped due to low confidence.

Tools
Biome

[error] 150-150: Change to an optional chain.

Unsafe fix: Change to an optional chain.

(lint/complexity/useOptionalChain)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Outside diff range, codebase verification and nitpick comments (1)
packages/server/postgres/migrations/1722527208553_SuggestAction-phase2.ts (1)

28-34: Consider defining constants outside the function.

Defining MAX_PG_PARAMS, PG_COLS, and BATCH_SIZE outside the function can improve readability and maintainability.

-  const MAX_PG_PARAMS = 65545
-  const PG_COLS = ['id', 'createdAt', 'priority', 'removedAt', 'type', 'teamId', 'userId'] as const
-  const BATCH_SIZE = Math.trunc(MAX_PG_PARAMS / PG_COLS.length)
+  const MAX_PG_PARAMS = 65545;
+  const PG_COLS = ['id', 'createdAt', 'priority', 'removedAt', 'type', 'teamId', 'userId'] as const;
+  const BATCH_SIZE = Math.trunc(MAX_PG_PARAMS / PG_COLS.length);

Comment on lines 36 to 84
let curcreatedAt = r.minval
let curId = r.minval
for (let i = 0; i < 1e6; i++) {
console.log('inserting row', i * BATCH_SIZE, curcreatedAt, curId)
const rawRowsToInsert = (await r
.table('SuggestedAction')
.between([curcreatedAt, curId], [r.maxval, r.maxval], {
index: 'createdAtId',
leftBound: 'open',
rightBound: 'closed'
})
.orderBy({index: 'createdAtId'})
.limit(BATCH_SIZE)
.pluck(...PG_COLS)
.run()) as SuggestedAction[]

const rowsToInsert = rawRowsToInsert.map((row) => ({
...row
}))
if (rowsToInsert.length === 0) break
const lastRow = rowsToInsert[rowsToInsert.length - 1]
curcreatedAt = lastRow.createdAt
curId = lastRow.id
try {
await pg
.insertInto('SuggestedAction')
.values(rowsToInsert)
.onConflict((oc) => oc.doNothing())
.execute()
} catch (e) {
await Promise.all(
rowsToInsert.map(async (row) => {
try {
await pg
.insertInto('SuggestedAction')
.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)
}
})
)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Efficient data migration but consider adding progress tracking.

The data migration logic is efficient, but adding progress tracking (e.g., percentage completed) can be helpful for large datasets.

-  for (let i = 0; i < 1e6; i++) {
+  const totalRows = await r.table('SuggestedAction').count().run();
+  for (let i = 0; i < Math.ceil(totalRows / BATCH_SIZE); i++) {
+    console.log(`Progress: ${((i * BATCH_SIZE) / totalRows) * 100}%`);
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let curcreatedAt = r.minval
let curId = r.minval
for (let i = 0; i < 1e6; i++) {
console.log('inserting row', i * BATCH_SIZE, curcreatedAt, curId)
const rawRowsToInsert = (await r
.table('SuggestedAction')
.between([curcreatedAt, curId], [r.maxval, r.maxval], {
index: 'createdAtId',
leftBound: 'open',
rightBound: 'closed'
})
.orderBy({index: 'createdAtId'})
.limit(BATCH_SIZE)
.pluck(...PG_COLS)
.run()) as SuggestedAction[]
const rowsToInsert = rawRowsToInsert.map((row) => ({
...row
}))
if (rowsToInsert.length === 0) break
const lastRow = rowsToInsert[rowsToInsert.length - 1]
curcreatedAt = lastRow.createdAt
curId = lastRow.id
try {
await pg
.insertInto('SuggestedAction')
.values(rowsToInsert)
.onConflict((oc) => oc.doNothing())
.execute()
} catch (e) {
await Promise.all(
rowsToInsert.map(async (row) => {
try {
await pg
.insertInto('SuggestedAction')
.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)
}
})
)
}
}
let curcreatedAt = r.minval
let curId = r.minval
const totalRows = await r.table('SuggestedAction').count().run();
for (let i = 0; i < Math.ceil(totalRows / BATCH_SIZE); i++) {
console.log(`Progress: ${((i * BATCH_SIZE) / totalRows) * 100}%`);
console.log('inserting row', i * BATCH_SIZE, curcreatedAt, curId)
const rawRowsToInsert = (await r
.table('SuggestedAction')
.between([curcreatedAt, curId], [r.maxval, r.maxval], {
index: 'createdAtId',
leftBound: 'open',
rightBound: 'closed'
})
.orderBy({index: 'createdAtId'})
.limit(BATCH_SIZE)
.pluck(...PG_COLS)
.run()) as SuggestedAction[]
const rowsToInsert = rawRowsToInsert.map((row) => ({
...row
}))
if (rowsToInsert.length === 0) break
const lastRow = rowsToInsert[rowsToInsert.length - 1]
curcreatedAt = lastRow.createdAt
curId = lastRow.id
try {
await pg
.insertInto('SuggestedAction')
.values(rowsToInsert)
.onConflict((oc) => oc.doNothing())
.execute()
} catch (e) {
await Promise.all(
rowsToInsert.map(async (row) => {
try {
await pg
.insertInto('SuggestedAction')
.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)
}
})
)
}
}

@mattkrick mattkrick merged commit 6c0cc36 into master Aug 5, 2024
7 checks passed
@mattkrick mattkrick deleted the chore/SuggestedAction-phase3 branch August 5, 2024 23:17
@github-actions github-actions bot mentioned this pull request Aug 6, 2024
24 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants