Skip to content

Commit

Permalink
feat(backend): Introduce OrganizationRoleAPI (#2177)
Browse files Browse the repository at this point in the history
* feat(backend): Introduce OrganizationRoleAPI

* test(backend): Update exports.test.ts

* test(clerk-sdk-node): Update exports

* test(nextjs): Update exports.test.ts.snap

* feat(backend): Address PR comments

* chore(backend): Rename OrganizationRole to Role
  • Loading branch information
panteliselef authored Nov 22, 2023
1 parent 0ce0edc commit b3a3dcd
Show file tree
Hide file tree
Showing 11 changed files with 190 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/cold-comics-serve.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': patch
---

Add OrganizationRoleAPI for CRUD operations regarding instance level organization roles.
138 changes: 138 additions & 0 deletions packages/backend/src/api/endpoints/OrganizationRoleApi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import { joinPaths } from '../../util/path';
import type { DeletedObject, Role } from '../resources';
import { AbstractAPI } from './AbstractApi';

const basePath = '/organizations_roles';

type GetRoleListParams = {
limit?: number;
offset?: number;
query?: string;
order_by?: string;
};

type CreateParams = {
/**
* A name of a role in a readable friendly format.
* F.e. `Teacher` or `Administrator`
*/
name: string;

/**
* A unique identifier that represents the role.
* F.e. `org:administrator`
*/
key: string;

/**
* A brief description of what the role represents or its intended use.
*/
description: string;

/**
* An array of permission ids that will be assigned to this role.
*/
permissions: string[];
};

type GetOrganizationRoleParams = { roleId: string };

type UpdateParams = {
/**
* A name of a role in a readable friendly format.
* F.e. `Teacher` or `Administrator`
* Passing undefined has no effect to the existing value.
*/
name?: string;

/**
* A unique identifier that represents the role.
* F.e. `org:administrator`
* Passing undefined has no effect to the existing value.
*/
key?: string;

/**
* A brief description of what the role represents or its intended use.
* Passing undefined has no effect to the existing value.
*/
description?: string;

/**
* An array of permission ids that will be assigned to this role.
* Passing undefined has no effect to the permission that already exist.
* Passing an empty array will override the existing permissions.
*/
permissions?: string[];
};

type RemovePermissionParams = {
permissionId: string;
roleId: string;
};

type AssignPermissionParams = RemovePermissionParams;

export class OrganizationRoleAPI extends AbstractAPI {
public async getOrganizationRoleList(params?: GetRoleListParams) {
return this.request<Role[]>({
method: 'GET',
path: basePath,
queryParams: params,
});
}

public async createOrganizationRole(params: CreateParams) {
return this.request<Role>({
method: 'POST',
path: basePath,
bodyParams: params,
});
}

public async getOrganizationRole(params: GetOrganizationRoleParams) {
this.requireId(params.roleId);

return this.request<Role>({
method: 'GET',
path: joinPaths(basePath, params.roleId),
});
}

public async updateOrganizationRole(roleId: string, params: UpdateParams) {
this.requireId(roleId);
return this.request<Role>({
method: 'PATCH',
path: joinPaths(basePath, roleId),
bodyParams: params,
});
}

public async deleteOrganizationRole(roleId: string) {
this.requireId(roleId);
return this.request<DeletedObject>({
method: 'DELETE',
path: joinPaths(basePath, roleId),
});
}

public async assignPermissionToRole(params: AssignPermissionParams) {
const { roleId, permissionId } = params;
this.requireId(roleId);
this.requireId(permissionId);
return this.request<Role>({
method: 'POST',
path: joinPaths(basePath, roleId, 'permission', permissionId),
});
}

public async removePermissionFromRole(params: RemovePermissionParams) {
const { roleId, permissionId } = params;
this.requireId(roleId);
this.requireId(permissionId);
return this.request<Role>({
method: 'DELETE',
path: joinPaths(basePath, roleId, 'permission', permissionId),
});
}
}
1 change: 1 addition & 0 deletions packages/backend/src/api/endpoints/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './EmailApi';
export * from './InterstitialApi';
export * from './InvitationApi';
export * from './OrganizationApi';
export * from './OrganizationRoleApi';
export * from './OrganizationPermissionApi';
export * from './PhoneNumberApi';
export * from './RedirectUrlApi';
Expand Down
2 changes: 2 additions & 0 deletions packages/backend/src/api/factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
InvitationAPI,
OrganizationAPI,
OrganizationPermissionAPI,
OrganizationRoleAPI,
PhoneNumberAPI,
RedirectUrlAPI,
SessionAPI,
Expand All @@ -30,6 +31,7 @@ export function createBackendApiClient(options: CreateBackendApiOptions) {
interstitial: new InterstitialAPI(request),
invitations: new InvitationAPI(request),
organizations: new OrganizationAPI(request),
organizationRoles: new OrganizationRoleAPI(request),
organizationPermissions: new OrganizationPermissionAPI(request),
phoneNumbers: new PhoneNumberAPI(request),
redirectUrls: new RedirectUrlAPI(request),
Expand Down
11 changes: 11 additions & 0 deletions packages/backend/src/api/resources/JSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export enum ObjectType {
Organization = 'organization',
OrganizationInvitation = 'organization_invitation',
OrganizationMembership = 'organization_membership',
Role = 'role',
Permission = 'permission',
PhoneNumber = 'phone_number',
RedirectUrl = 'redirect_url',
Expand Down Expand Up @@ -174,6 +175,16 @@ export interface OrganizationMembershipPublicUserDataJSON {
user_id: string;
}

export interface RoleJSON extends ClerkResourceJSON {
object: ObjectType.Role;
name: string;
key: string;
description: string;
permissions: PermissionJSON[];
created_at: number;
updated_at: number;
}

export interface PermissionJSON extends ClerkResourceJSON {
object: ObjectType.Permission;
id: string;
Expand Down
26 changes: 26 additions & 0 deletions packages/backend/src/api/resources/Role.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { RoleJSON } from './JSON';
import { Permission } from './Permission';

export class Role {
constructor(
readonly id: string,
readonly name: string,
readonly key: string,
readonly description: string,
readonly permissions: Permission[] = [],
readonly createdAt: number,
readonly updatedAt: number,
) {}

static fromJSON(data: RoleJSON): Role {
return new Role(
data.id,
data.name,
data.key,
data.description,
(data.permissions || []).map(x => Permission.fromJSON(x)),
data.created_at,
data.updated_at,
);
}
}
1 change: 1 addition & 0 deletions packages/backend/src/api/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export * from './Invitation';
export * from './JSON';
export * from './OauthAccessToken';
export * from './Organization';
export * from './Role';
export * from './Permission';
export * from './OrganizationInvitation';
export * from './OrganizationMembership';
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/exports.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export default (QUnit: QUnit) => {
'Permission',
'PhoneNumber',
'RedirectUrl',
'Role',
'SMSMessage',
'Session',
'SignInToken',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ exports[`/server public exports should not include a breaking change 1`] = `
"Permission",
"PhoneNumber",
"RedirectUrl",
"Role",
"SMSMessage",
"Session",
"SignInToken",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ exports[`module exports should not change unless explicitly set 1`] = `
"Permission",
"PhoneNumber",
"RedirectUrl",
"Role",
"SMSMessage",
"Session",
"SignInToken",
Expand Down Expand Up @@ -54,6 +55,7 @@ exports[`module exports should not change unless explicitly set 1`] = `
"loadInterstitialFromLocal",
"makeAuthObjectSerializable",
"organizationPermissions",
"organizationRoles",
"organizations",
"phoneNumbers",
"prunePrivateMetadata",
Expand Down
2 changes: 2 additions & 0 deletions packages/sdk-node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const {
emails,
invitations,
organizations,
organizationRoles,
organizationPermissions,
clients,
allowlistIdentifiers,
Expand All @@ -40,6 +41,7 @@ export {
emails,
invitations,
organizations,
organizationRoles,
organizationPermissions,
phoneNumbers,
sessions,
Expand Down

0 comments on commit b3a3dcd

Please sign in to comment.