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

feat: New Organization Schema along with the ability to invite existing users to an organization #13002

Merged
merged 70 commits into from
Feb 2, 2024

Conversation

hariombalhara
Copy link
Member

@hariombalhara hariombalhara commented Jan 3, 2024

What does this PR do?

Existing cal.com user can be invited to an organization now.
Fixes #13013
Fixes #13002
Fixes #13367
Fixes #13359

Demo - https://www.loom.com/share/a980bb3fc14140b99355aed36b639134

image

This is a big PR but it wasn't possible to break it down significantly further.

Schema Changes

  • Deprecate organizationId and organization from User table
    • Will be removed in near future.
  • Instead have Profile table with organizationId and userId relation.

Seeder

  • Added organization seeder that adds Acme and Dunder Mifflin organizations with some event-types and teams.

Repositories

  • Some repositories created Profile, User, Membership which mostly abstract out the reusable queries together.
  • A repository is a class at the moment just to be a collection of methods, we can do other OOP stuff later on.

Brainstorming and Release Plan here

https://docs.google.com/document/d/1ii3eyn0F0LaUCrX1c58YgdMt2V7xZWI2AGX1yKKM2Ik/edit

New Organization Accept Invite View inside Teams itself

image

TODO

  • Write migration to create Profile table and populate it with all org user entries
  • Open a separate PR that would go live before this. [Will do this once the PR is fully reviewed and is ready to be merged. Chose against doing it. It is a bit of effort to separate the migration and I see no risk with going with the migration along with the changes as part of this PR.

Type of change

  • New feature (non-breaking change which adds functionality)

How should this be tested?

There is a lot to test

Mandatory Tasks

  • Make sure you have self-reviewed the code. A decent size PR without self-review might be rejected.

Checklist

  • I haven't added tests that prove my fix is effective or that my feature works

Sorry, something went wrong.

Copy link

vercel bot commented Jan 3, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
ai ✅ Ready (Inspect) Visit Preview 💬 Add feedback Feb 2, 2024 6:54pm
api ❌ Failed (Inspect) Feb 2, 2024 6:54pm
dev ❌ Failed (Inspect) Feb 2, 2024 6:54pm
5 Ignored Deployments
Name Status Preview Comments Updated (UTC)
cal ⬜️ Ignored (Inspect) Visit Preview Feb 2, 2024 6:54pm
cal-demo ⬜️ Ignored (Inspect) Visit Preview Feb 2, 2024 6:54pm
calcom-web-canary ⬜️ Ignored (Inspect) Visit Preview Feb 2, 2024 6:54pm
qa ⬜️ Ignored (Inspect) Visit Preview Feb 2, 2024 6:54pm
ui ⬜️ Ignored (Inspect) Visit Preview Feb 2, 2024 6:54pm

Copy link
Contributor

github-actions bot commented Jan 3, 2024

Thank you for following the naming conventions! 🙏 Feel free to join our discord and post your PR link.

@hariombalhara
Copy link
Member Author

hariombalhara commented Jan 3, 2024

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

Current dependencies on/for this PR:

This stack of pull requests is managed by Graphite.

@github-actions github-actions bot added the ❗️ migrations contains migration files label Jan 3, 2024
@keithwillcode keithwillcode added the core area: core, team members only label Jan 3, 2024

@@unique([email])
@@unique([email, username])
@@unique([username, organizationId])
Copy link
Member Author

Choose a reason for hiding this comment

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

We will drop organizationId in separate deployment. If we remove it now, there would be username conflicts in the table.

Copy link
Contributor

github-actions bot commented Jan 3, 2024

📦 Next.js Bundle Analysis for @calcom/web

This analysis was generated by the Next.js Bundle Analysis action. 🤖

Seventy-one Pages Changed Size

The following pages changed size from the code in this PR compared to its base branch:

Page Size (compressed) First Load % of Budget (350 KB)
/[user]/[type] 265.07 KB 453.2 KB 129.49% (🟡 +0.29%)
/[user]/[type]/embed 265.08 KB 453.2 KB 129.49% (🟡 +0.29%)
/apps 279.12 KB 467.25 KB 133.50% (🟡 +2.23%)
/apps/[slug] 296.63 KB 484.76 KB 138.50% (🟡 +2.23%)
/apps/categories 256.81 KB 444.93 KB 127.12% (🟡 +2.23%)
/apps/categories/[category] 261.12 KB 449.24 KB 128.35% (🟡 +2.23%)
/apps/installed/[category] 280.2 KB 468.33 KB 133.81% (🟡 +2.23%)
/auth/setup 149.55 KB 337.67 KB 96.48% (🟢 -0.27%)
/availability/troubleshoot 172.7 KB 360.83 KB 103.09% (🟡 +0.25%)
/booking/[uid] 190.15 KB 378.28 KB 108.08% (🟢 -0.23%)
/booking/[uid]/embed 190.16 KB 378.28 KB 108.08% (🟢 -0.23%)
/d/[link]/[slug] 264.9 KB 453.03 KB 129.44% (🟡 +0.29%)
/enterprise 257.28 KB 445.4 KB 127.26% (🟡 +2.22%)
/more 256.43 KB 444.56 KB 127.02% (🟡 +2.22%)
/org/[orgSlug] 236.92 KB 425.05 KB 121.44% (🟡 +2.05%)
/org/[orgSlug]/[user] 243.33 KB 431.45 KB 123.27% (🟡 +2.03%)
/org/[orgSlug]/[user]/[type] 265.27 KB 453.4 KB 129.54% (🟡 +0.30%)
/org/[orgSlug]/[user]/[type]/embed 265.3 KB 453.42 KB 129.55% (🟡 +0.30%)
/org/[orgSlug]/[user]/embed 243.35 KB 431.48 KB 123.28% (🟡 +2.02%)
/org/[orgSlug]/embed 236.94 KB 425.07 KB 121.45% (🟡 +2.04%)
/org/[orgSlug]/instant-meeting/team/[slug]/[type] 264.91 KB 453.04 KB 129.44% (🟡 +0.30%)
/org/[orgSlug]/team/[slug] 236.93 KB 425.06 KB 121.45% (🟡 +2.04%)
/org/[orgSlug]/team/[slug]/[type] 264.95 KB 453.07 KB 129.45% (🟡 +0.30%)
/settings/admin 263.14 KB 451.27 KB 128.93% (🟡 +2.24%)
/settings/admin/apps 274.62 KB 462.74 KB 132.21% (🟡 +1.72%)
/settings/admin/apps/[category] 274.57 KB 462.7 KB 132.20% (🟡 +1.71%)
/settings/admin/flags 266.9 KB 455.03 KB 130.01% (🟡 +2.23%)
/settings/admin/impersonation 263.44 KB 451.56 KB 129.02% (🟡 +2.23%)
/settings/admin/oAuth 275.15 KB 463.28 KB 132.36% (🟡 +2.24%)
/settings/admin/orgMigrations/_OrgMigrationLayout 255.91 KB 444.03 KB 126.87% (🟡 +2.23%)
/settings/admin/organizations 265.16 KB 453.29 KB 129.51% (🟡 +2.23%)
/settings/admin/organizations/[id]/edit 263.66 KB 451.79 KB 129.08% (🟡 +2.23%)
/settings/admin/users 265.86 KB 453.99 KB 129.71% (🟡 +2.23%)
/settings/billing 263.34 KB 451.47 KB 128.99% (🟡 +2.23%)
/settings/developer/api-keys 267.77 KB 455.89 KB 130.25% (🟡 +2.24%)
/settings/developer/webhooks 267.69 KB 455.81 KB 130.23% (🟡 +2.23%)
/settings/developer/webhooks/[id] 268.75 KB 456.87 KB 130.54% (🟡 +2.23%)
/settings/developer/webhooks/new 268.79 KB 456.92 KB 130.55% (🟡 +2.23%)
/settings/my-account/calendars 274.13 KB 462.26 KB 132.07% (🟡 +2.24%)
/settings/my-account/conferencing 275.02 KB 463.14 KB 132.33% (🟡 +2.23%)
/settings/my-account/general 351.49 KB 539.61 KB 154.17% (🟡 +2.24%)
/settings/my-account/out-of-office 267.56 KB 455.69 KB 130.20% (🟡 +2.23%)
/settings/my-account/profile 399.91 KB 588.04 KB 168.01% (🟡 +2.08%)
/settings/organizations/[id]/about 152.38 KB 340.51 KB 97.29% (🟡 +0.25%)
/settings/organizations/[id]/add-teams 152.44 KB 340.56 KB 97.30% (🟡 +0.26%)
/settings/organizations/[id]/set-password 152.37 KB 340.49 KB 97.28% (🟡 +0.26%)
/settings/organizations/appearance 286.76 KB 474.88 KB 135.68% (🟡 +2.23%)
/settings/organizations/billing 263.38 KB 451.51 KB 129.00% (🟡 +2.23%)
/settings/organizations/general 344.03 KB 532.16 KB 152.04% (🟡 +2.24%)
/settings/organizations/members 435.88 KB 624.01 KB 178.29% (🟡 +2.23%)
/settings/organizations/new 152.39 KB 340.51 KB 97.29% (🟡 +0.25%)
/settings/organizations/profile 396.9 KB 585.02 KB 167.15% (🟡 +2.07%)
/settings/organizations/teams/other 264.2 KB 452.32 KB 129.24% (🟡 +2.23%)
/settings/organizations/teams/other/[id]/appearance 275.84 KB 463.97 KB 132.56% (🟡 +2.24%)
/settings/organizations/teams/other/[id]/members 270.73 KB 458.86 KB 131.10% (🟡 +2.24%)
/settings/organizations/teams/other/[id]/profile 469.07 KB 657.19 KB 187.77% (🟡 +2.23%)
/settings/security/impersonation 268.46 KB 456.58 KB 130.45% (🟡 +2.23%)
/settings/security/sso 273.44 KB 461.57 KB 131.88% (🟡 +2.23%)
/settings/security/two-factor-auth 272.28 KB 460.41 KB 131.55% (🟡 +2.23%)
/settings/teams 262.88 KB 451 KB 128.86% (🟡 +2.23%)
/settings/teams/[id]/appearance 275.83 KB 463.96 KB 132.56% (🟡 +2.23%)
/settings/teams/[id]/billing 263.38 KB 451.51 KB 129.00% (🟡 +2.23%)
/settings/teams/[id]/profile 469.9 KB 658.02 KB 188.01% (🟡 +2.23%)
/settings/teams/[id]/sso 273.97 KB 462.1 KB 132.03% (🟡 +2.23%)
/settings/teams/new 206.8 KB 394.93 KB 112.84% (🟡 +4.64%)
/team/[slug] 236.89 KB 425.01 KB 121.43% (🟡 +2.05%)
/team/[slug]/[type] 264.91 KB 453.04 KB 129.44% (🟡 +0.29%)
/team/[slug]/[type]/embed 264.94 KB 453.07 KB 129.45% (🟡 +0.29%)
/team/[slug]/embed 236.93 KB 425.05 KB 121.44% (🟡 +2.05%)
/teams 256.71 KB 444.84 KB 127.10% (🟡 +2.22%)
/upgrade 256.88 KB 445.01 KB 127.15% (🟡 +2.22%)
Details

Only the gzipped size is provided here based on an expert tip.

First Load is the size of the global bundle plus the bundle for the individual page. If a user were to show up to your website and land on a given page, the first load size represents the amount of javascript that user would need to download. If next/link is used, subsequent page loads would only need to download that page's bundle (the number in the "Size" column), since the global bundle has already been downloaded.

Any third party scripts you have added directly to your app using the <script> tag are not accounted for in this analysis

The "Budget %" column shows what percentage of your performance budget the First Load total takes up. For example, if your budget was 100kb, and a given page's first load size was 10kb, it would be 10% of your budget. You can also see how much this has increased or decreased compared to the base branch of your PR. If this percentage has increased by 20% or more, there will be a red status indicator applied, indicating that special attention should be given to this. If you see "+/- <0.01%" it means that there was a change in bundle size, but it is a trivial enough amount that it can be ignored.

@hariombalhara hariombalhara force-pushed the organizationId-column branch from 395e5e5 to af0102e Compare January 6, 2024 06:00
@hariombalhara hariombalhara force-pushed the organizationId-column branch from af0102e to 0f6dd1b Compare January 8, 2024 10:59
@PeerRich PeerRich added the High priority Created by Linear-GitHub Sync label Jan 8, 2024
@hariombalhara hariombalhara force-pushed the organizationId-column branch from 0f6dd1b to 4bca8f3 Compare January 9, 2024 16:20
Copy link
Contributor

@joeauyeung joeauyeung left a comment

Choose a reason for hiding this comment

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

This was such a huge undertaking like damn! I looked through the code and left some comments. I still want to manually test this.

I want to bring up I'm not sure I like the idea of the naming of "profile". While reading through the PR I had to get a grasp of what a profile in this context is. From my understanding, it's the org information and how it relates to the user. I think for maintainability we should name it something more obvious. What about userOrgRepository & userOrgProfile or something along those lines?

Comment on lines +426 to +437
async jwt({
// Always available but with a little difference in value
token,
// Available only in case of signIn, signUp or useSession().update call.
trigger,
// Available when useSession().update is called. The value will be the POST data
session,
// Available only in the first call once the user signs in. Not available in subsequent calls
user,
// Available only in the first call once the user signs in. Not available in subsequent calls
account,
}) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Love this

@@ -185,7 +186,7 @@ export async function refreshCredentials(
* Gets credentials from the user, team, and org if applicable
*
*/
export const getAllCredentials = async (
Copy link
Contributor

Choose a reason for hiding this comment

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

Heads up the return type of this function is needed in NewSeatedBookingObject

Copy link
Member Author

Choose a reason for hiding this comment

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

That's interesting. I don't see it imported anywhere. Found a reference but it's not imported there
image

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm interesting I thought it had to be imported. You're right though and type checks are passing so I think we're good.

@@ -900,18 +904,22 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
<div className="flex h-full flex-col justify-between py-3 lg:pt-4">
<header className="todesktop:-mt-3 todesktop:flex-col-reverse todesktop:[-webkit-app-region:drag] items-center justify-between md:hidden lg:flex">
{orgBranding ? (
Copy link
Contributor

Choose a reason for hiding this comment

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

Have we thought of enabling the profile switcher for people outside of orgs? I have a personal and a work Cal.com account that I switch between.

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you intend to use the same email for those or different emails? For different emails see this #13408 (comment).

For the same email, though it's not the focus but it's still doable by having some negative values of organizationId, though not sure if it's the best approach.

Copy link
Contributor

Choose a reason for hiding this comment

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

My idea was for different emails. There might be some users that have a personal email and a work email. Their work emails would not be in an org. We could always follow up with another PR.

@hariombalhara
Copy link
Member Author

I want to bring up I'm not sure I like the idea of the naming of "profile". While reading through the PR I had to get a grasp of what a profile in this context is. From my understanding, it's the org information and how it relates to the user. I think for maintainability we should name it something more obvious. What about userOrgRepository & userOrgProfile or something along those lines?

Actually Profile is going to hold all the profiles including Personal Profile which is currently stored in User table in a way. So, as soon as a user is added to an organization, the User table stops being acting as his profile. After that, to create his personal profile, we would create an entry in Profile table only with organizationId=0 probably. This is how he would be able to switch b/w Personal and Organization Profile(both being stored in Profile table)

@hariombalhara
Copy link
Member Author

hariombalhara commented Feb 1, 2024

Thank you so much for the detailed review @joeauyeung !! Really appreciate going through the huge PR for even minute details 🙏

I have addressed all the feedback.

Copy link
Contributor

@joeauyeung joeauyeung left a comment

Choose a reason for hiding this comment

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

Tested it out manually. I found a lot of small stuff that can honestly be in a follow up PR. We don't need to add more to this one. I did find one blocking thing with the profile switcher but other than that it looks good. Let me know if I missed any test cases.

Test Cases

  • Inviting member to org w/o Cal account to auto join org
  • Inviting member to an org team w/o Cal account and auto join org
  • Inviting existing member to org, throws error
  • Editing member role
  • Inviting org member to a non org team
  • Inviting a member w/o the same org domain & w/o a Cal account to the org & team
  • Inviting existing user to org w/o same org domain to the org & team
  • Inviting an existing org member to another org

Blocking

  • When a user is a member of two orgs, when I go to the profile switcher it stays on the first org instead of switching over.

Minor feedback (Can follow up)

  • On settings shell, org avatar is broken
    CleanShot 2024-02-01 at 12.01.36@2x.png

  • On org member's page, the avatars are falling back to the default
    CleanShot 2024-02-01 at 12.24.48@2x.png

    • Interesting though, when inviting an org member to a non org team the avatar renders as expected
      CleanShot 2024-02-01 at 13.08.42@2x.png
  • On org invite, create account image is broken
    CleanShot 2024-02-01 at 12.04.42@2x.png

  • When creating a new team under an org and inviting members, the user avatar flashes but then is replaced with the org logo
    CleanShot 2024-02-01 at 12.27.01@2x.png

  • On the switcher the org logo isn't rendering. I also think we should add an option to "Add another account"
    CleanShot 2024-02-01 at 13.01.55@2x.png

  • I think if we're allowing org members to be in non-org / other orgs we should have some separation on the teams page
    CleanShot 2024-02-01 at 13.12.52@2x.png

  • When accepting an org invite from an existing user, the organization doesn't render until the user has logged out. Should we force a logout after accepting?

@hariombalhara
Copy link
Member Author

Thanks for the testing @joeauyeung. I have Fixed the profile switcher, it was a regression. https://www.loom.com/share/6bd6d89766764fa38e7e43b26af84bb5

Also, we aren't enable Profile Switcher in this release - Here is the releases plan https://docs.google.com/document/d/1ii3eyn0F0LaUCrX1c58YgdMt2V7xZWI2AGX1yKKM2Ik/edit#heading=h.b2ecpp7kmwhx

@hariombalhara
Copy link
Member Author

"When accepting an org invite from an existing user, the organization doesn't render until the user has logged out. Should we force a logout after accepting?" -After accepting the invite a refresh of the app is needed. I don't think logout is necessary.

This is in my todo list and I think a refetch for session should fix the issue.

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Copy link
Contributor

@joeauyeung joeauyeung left a comment

Choose a reason for hiding this comment

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

I think we're at a good point for V1. Awesome work @hariombalhara, this was no small feat. I'm excited for V2!

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
Comment on lines +113 to +129
team: {
include: {
eventTypes: true,
},
},
hashedLink: true,
users: true,
children: {
include: {
users: true,
},
},
hosts: {
include: {
user: true,
},
},
Copy link
Member

Choose a reason for hiding this comment

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

I don't want to be a downer here but this is causing leakage of team members undecoded avatar URIs and passwords. I'm getting payloads of about 30MB locally with production-like data.

Copy link
Member

Choose a reason for hiding this comment

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working community Created by Linear-GitHub Sync consumer core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO High priority Created by Linear-GitHub Sync Low priority Created by Linear-GitHub Sync ❗️ migrations contains migration files organizations area: organizations, orgs teams area: teams, round robin, collective, managed event-types
Projects
None yet
6 participants