Skip to content

Commit

Permalink
fix(clerk-js): Emit session when permissions or role of the active me…
Browse files Browse the repository at this point in the history
…mberships change (#2073)

* fix(clerk-js): Emit session when permissions or role of the active memberships change

* chore(clerk-js): Add changeset
  • Loading branch information
panteliselef authored Nov 8, 2023
1 parent 3bdee6e commit 5f49568
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 24 deletions.
5 changes: 5 additions & 0 deletions .changeset/dry-students-reflect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/clerk-js': patch
---

Emit session when permissions or role of the active memberships change.
66 changes: 43 additions & 23 deletions packages/clerk-js/src/core/resources/Organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
GetInvitationsParams,
GetMembershipRequestParams,
GetMemberships,
GetMembershipsParams,
GetPendingInvitationsParams,
GetRolesParams,
InviteMemberParams,
Expand All @@ -26,7 +27,6 @@ import type {
UpdateMembershipParams,
UpdateOrganizationParams,
} from '@clerk/types';
import type { GetMembershipsParams } from '@clerk/types';

import { unixEpochToDate } from '../../utils/date';
import { convertPageToOffset } from '../../utils/pagesToOffset';
Expand Down Expand Up @@ -109,11 +109,16 @@ export class Organization extends BaseResource implements OrganizationResource {
};

getRoles = async (getRolesParams?: GetRolesParams) => {
return await BaseResource._fetch({
path: `/organizations/${this.id}/roles`,
method: 'GET',
search: convertPageToOffset(getRolesParams) as any,
}).then(res => {
return await BaseResource._fetch(
{
path: `/organizations/${this.id}/roles`,
method: 'GET',
search: convertPageToOffset(getRolesParams) as any,
},
{
forceUpdateClient: true,
},
).then(res => {
const { data: roles, total_count } = res?.response as unknown as ClerkPaginatedResponse<RoleJSON>;

return {
Expand All @@ -126,11 +131,16 @@ export class Organization extends BaseResource implements OrganizationResource {
getDomains = async (
getDomainParams?: GetDomainsParams,
): Promise<ClerkPaginatedResponse<OrganizationDomainResource>> => {
return await BaseResource._fetch({
path: `/organizations/${this.id}/domains`,
method: 'GET',
search: convertPageToOffset(getDomainParams) as any,
})
return await BaseResource._fetch(
{
path: `/organizations/${this.id}/domains`,
method: 'GET',
search: convertPageToOffset(getDomainParams) as any,
},
{
forceUpdateClient: true,
},
)
.then(res => {
const { data: invites, total_count } =
res?.response as unknown as ClerkPaginatedResponse<OrganizationDomainJSON>;
Expand Down Expand Up @@ -197,13 +207,18 @@ export class Organization extends BaseResource implements OrganizationResource {
deprecated('offset', 'Use `initialPage` instead in Organization.limit.', 'organization:getMemberships:offset');
}

return await BaseResource._fetch({
path: `/organizations/${this.id}/memberships`,
method: 'GET',
search: isDeprecatedParams
? getMembershipsParams
: (convertPageToOffset(getMembershipsParams as unknown as any) as any),
})
return await BaseResource._fetch(
{
path: `/organizations/${this.id}/memberships`,
method: 'GET',
search: isDeprecatedParams
? getMembershipsParams
: (convertPageToOffset(getMembershipsParams as unknown as any) as any),
},
{
forceUpdateClient: true,
},
)
.then(res => {
if (isDeprecatedParams) {
const organizationMembershipsJSON = res?.response as unknown as OrganizationMembershipJSON[];
Expand Down Expand Up @@ -248,11 +263,16 @@ export class Organization extends BaseResource implements OrganizationResource {
getInvitations = async (
getInvitationsParams?: GetInvitationsParams,
): Promise<ClerkPaginatedResponse<OrganizationInvitationResource>> => {
return await BaseResource._fetch({
path: `/organizations/${this.id}/invitations`,
method: 'GET',
search: convertPageToOffset(getInvitationsParams) as any,
})
return await BaseResource._fetch(
{
path: `/organizations/${this.id}/invitations`,
method: 'GET',
search: convertPageToOffset(getInvitationsParams) as any,
},
{
forceUpdateClient: true,
},
)
.then(res => {
const { data: requests, total_count } =
res?.response as unknown as ClerkPaginatedResponse<OrganizationInvitationJSON>;
Expand Down
25 changes: 24 additions & 1 deletion packages/clerk-js/src/utils/memoizeStateListenerCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,11 @@ function clientChanged(prev: ClientResource, next: ClientResource): boolean {
}

function sessionChanged(prev: SessionResource, next: SessionResource): boolean {
return prev.id !== next.id || prev.updatedAt.getTime() < next.updatedAt.getTime();
return (
prev.id !== next.id ||
prev.updatedAt.getTime() < next.updatedAt.getTime() ||
sessionUserMembershipPermissionsChanged(next, prev)
);
}

function userChanged(prev: UserResource, next: UserResource): boolean {
Expand All @@ -45,6 +49,25 @@ function userMembershipsChanged(prev: UserResource, next: UserResource): boolean
);
}

function sessionUserMembershipPermissionsChanged(prev: SessionResource, next: SessionResource): boolean {
if (prev.lastActiveOrganizationId !== next.lastActiveOrganizationId) {
return true;
}

const prevActiveMembership = prev.user?.organizationMemberships?.find(
mem => mem.organization.id === prev.lastActiveOrganizationId,
);

const nextActiveMembership = next.user?.organizationMemberships?.find(
mem => mem.organization.id === prev.lastActiveOrganizationId,
);

return (
prevActiveMembership?.role !== nextActiveMembership?.role ||
prevActiveMembership?.permissions?.length !== nextActiveMembership?.permissions?.length
);
}

// TODO: Decide if this belongs in the resources
function resourceChanged<T extends AcceptedResource | undefined | null>(prev: T, next: T): boolean {
// support the setSession undefined intermediate state
Expand Down

0 comments on commit 5f49568

Please sign in to comment.