Path: blob/main/components/dashboard/src/service/json-rpc-organization-client.ts
2500 views
/**1* Copyright (c) 2023 Gitpod GmbH. All rights reserved.2* Licensed under the GNU Affero General Public License (AGPL).3* See License.AGPL.txt in the project root for license information.4*/56import { PartialMessage, PlainMessage } from "@bufbuild/protobuf";7import { CallOptions, PromiseClient } from "@connectrpc/connect";8import { OrganizationService } from "@gitpod/public-api/lib/gitpod/v1/organization_connect";9import {10CreateOrganizationRequest,11CreateOrganizationResponse,12DeleteOrganizationMemberRequest,13DeleteOrganizationMemberResponse,14DeleteOrganizationRequest,15DeleteOrganizationResponse,16GetOrganizationInvitationRequest,17GetOrganizationInvitationResponse,18GetOrganizationMaintenanceModeRequest,19GetOrganizationMaintenanceModeResponse,20GetOrganizationRequest,21GetOrganizationResponse,22GetOrganizationSettingsRequest,23GetOrganizationSettingsResponse,24GetMaintenanceNotificationRequest,25GetMaintenanceNotificationResponse,26JoinOrganizationRequest,27JoinOrganizationResponse,28ListOrganizationMembersRequest,29ListOrganizationMembersResponse,30ListOrganizationWorkspaceClassesRequest,31ListOrganizationWorkspaceClassesResponse,32ListOrganizationsRequest,33ListOrganizationsResponse,34OrganizationSettings,35ResetOrganizationInvitationRequest,36ResetOrganizationInvitationResponse,37SetOrganizationMaintenanceModeRequest,38SetOrganizationMaintenanceModeResponse,39SetMaintenanceNotificationRequest,40SetMaintenanceNotificationResponse,41UpdateOrganizationMemberRequest,42UpdateOrganizationMemberResponse,43UpdateOrganizationRequest,44UpdateOrganizationResponse,45UpdateOrganizationSettingsRequest,46UpdateOrganizationSettingsResponse,47} from "@gitpod/public-api/lib/gitpod/v1/organization_pb";48import { getGitpodService } from "./service";49import { converter } from "./public-api";50import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";5152export class JsonRpcOrganizationClient implements PromiseClient<typeof OrganizationService> {53async createOrganization(54request: PartialMessage<CreateOrganizationRequest>,55options?: CallOptions | undefined,56): Promise<CreateOrganizationResponse> {57if (!request.name) {58throw new ApplicationError(ErrorCodes.BAD_REQUEST, "name is required");59}60const result = await getGitpodService().server.createTeam(request.name);61return new CreateOrganizationResponse({62organization: converter.toOrganization(result),63});64}6566async listOrganizationWorkspaceClasses(67request: PartialMessage<ListOrganizationWorkspaceClassesRequest>,68options?: CallOptions | undefined,69): Promise<ListOrganizationWorkspaceClassesResponse> {70if (!request.organizationId) {71throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");72}73const list = await getGitpodService().server.getOrgWorkspaceClasses(request.organizationId);74return new ListOrganizationWorkspaceClassesResponse({75workspaceClasses: list.map((e) => converter.toWorkspaceClass(e)),76});77}7879async getOrganization(80request: PartialMessage<GetOrganizationRequest>,81options?: CallOptions | undefined,82): Promise<GetOrganizationResponse> {83if (!request.organizationId) {84throw new ApplicationError(ErrorCodes.BAD_REQUEST, "id is required");85}86const result = await getGitpodService().server.getTeam(request.organizationId);8788return new GetOrganizationResponse({89organization: converter.toOrganization(result),90});91}9293async updateOrganization(94request: PartialMessage<UpdateOrganizationRequest>,95options?: CallOptions | undefined,96): Promise<UpdateOrganizationResponse> {97if (!request.organizationId) {98throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");99}100if (!request.name) {101throw new ApplicationError(ErrorCodes.BAD_REQUEST, "name is required");102}103await getGitpodService().server.updateTeam(request.organizationId, {104name: request.name,105});106return new UpdateOrganizationResponse();107}108109async listOrganizations(110request: PartialMessage<ListOrganizationsRequest>,111options?: CallOptions | undefined,112): Promise<ListOrganizationsResponse> {113const result = await getGitpodService().server.getTeams();114return new ListOrganizationsResponse({115organizations: result.map((team) => converter.toOrganization(team)),116});117}118119async deleteOrganization(120request: PartialMessage<DeleteOrganizationRequest>,121options?: CallOptions | undefined,122): Promise<DeleteOrganizationResponse> {123if (!request.organizationId) {124throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");125}126await getGitpodService().server.deleteTeam(request.organizationId);127return new DeleteOrganizationResponse();128}129130async getOrganizationInvitation(131request: PartialMessage<GetOrganizationInvitationRequest>,132options?: CallOptions | undefined,133): Promise<GetOrganizationInvitationResponse> {134if (!request.organizationId) {135throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");136}137const result = await getGitpodService().server.getGenericInvite(request.organizationId);138return new GetOrganizationInvitationResponse({139invitationId: result.id,140});141}142143async joinOrganization(144request: PartialMessage<JoinOrganizationRequest>,145options?: CallOptions | undefined,146): Promise<JoinOrganizationResponse> {147if (!request.invitationId) {148throw new ApplicationError(ErrorCodes.BAD_REQUEST, "invitationId is required");149}150const result = await getGitpodService().server.joinTeam(request.invitationId);151return new JoinOrganizationResponse({152organizationId: result.id,153});154}155156async resetOrganizationInvitation(157request: PartialMessage<ResetOrganizationInvitationRequest>,158options?: CallOptions | undefined,159): Promise<ResetOrganizationInvitationResponse> {160if (!request.organizationId) {161throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");162}163const newInvite = await getGitpodService().server.resetGenericInvite(request.organizationId);164return new ResetOrganizationInvitationResponse({165invitationId: newInvite.id,166});167}168169async listOrganizationMembers(170request: PartialMessage<ListOrganizationMembersRequest>,171options?: CallOptions | undefined,172): Promise<ListOrganizationMembersResponse> {173if (!request.organizationId) {174throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");175}176const result = await getGitpodService().server.getTeamMembers(request.organizationId);177return new ListOrganizationMembersResponse({178members: result.map((member) => converter.toOrganizationMember(member)),179});180}181182async updateOrganizationMember(183request: PartialMessage<UpdateOrganizationMemberRequest>,184options?: CallOptions | undefined,185): Promise<UpdateOrganizationMemberResponse> {186if (!request.organizationId) {187throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");188}189if (!request.userId) {190throw new ApplicationError(ErrorCodes.BAD_REQUEST, "userId is required");191}192if (!request.role) {193throw new ApplicationError(ErrorCodes.BAD_REQUEST, "role is required");194}195await getGitpodService().server.setTeamMemberRole(196request.organizationId,197request.userId,198converter.fromOrgMemberRole(request.role),199);200return new UpdateOrganizationMemberResponse();201}202203async deleteOrganizationMember(204request: PartialMessage<DeleteOrganizationMemberRequest>,205options?: CallOptions | undefined,206): Promise<DeleteOrganizationMemberResponse> {207if (!request.organizationId) {208throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");209}210if (!request.userId) {211throw new ApplicationError(ErrorCodes.BAD_REQUEST, "userId is required");212}213await getGitpodService().server.removeTeamMember(request.organizationId, request.userId);214return new DeleteOrganizationMemberResponse();215}216217async getOrganizationSettings(218request: PartialMessage<GetOrganizationSettingsRequest>,219options?: CallOptions | undefined,220): Promise<GetOrganizationSettingsResponse> {221if (!request.organizationId) {222throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");223}224const result = await getGitpodService().server.getOrgSettings(request.organizationId);225return new GetOrganizationSettingsResponse({226settings: converter.toOrganizationSettings(result),227});228}229230async updateOrganizationSettings(231request: PartialMessage<UpdateOrganizationSettingsRequest>,232options?: CallOptions | undefined,233): Promise<UpdateOrganizationSettingsResponse> {234if (!request.organizationId) {235throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");236}237238if (239request.restrictedEditorNames &&240request.restrictedEditorNames.length > 0 &&241!request.updateRestrictedEditorNames242) {243throw new ApplicationError(244ErrorCodes.BAD_REQUEST,245"updateRestrictedEditorNames is required to be true to update restrictedEditorNames",246);247}248249if (250request.allowedWorkspaceClasses &&251request.allowedWorkspaceClasses.length > 0 &&252!request.updateAllowedWorkspaceClasses253) {254throw new ApplicationError(255ErrorCodes.BAD_REQUEST,256"updateAllowedWorkspaceClasses is required to be true to update allowedWorkspaceClasses",257);258}259260if (261request.pinnedEditorVersions &&262Object.keys(request.pinnedEditorVersions).length > 0 &&263!request.updatePinnedEditorVersions264) {265throw new ApplicationError(266ErrorCodes.BAD_REQUEST,267"updatePinnedEditorVersions is required to be true to update pinnedEditorVersions",268);269}270271if (request.roleRestrictions && request.roleRestrictions.length > 0 && !request.updateRoleRestrictions) {272throw new ApplicationError(273ErrorCodes.BAD_REQUEST,274"updateRoleRestrictions is required to be true when updating roleRestrictions",275);276}277if (278request.onboardingSettings?.recommendedRepositories &&279request.onboardingSettings.recommendedRepositories.length > 0 &&280!request.onboardingSettings.updateRecommendedRepositories281) {282throw new ApplicationError(283ErrorCodes.BAD_REQUEST,284"recommendedRepositories can only be set when updateRecommendedRepositories is true",285);286}287288// gpl: We accept the little bit of uncertainty here because a) the partial/not-partial mismatch is only about289// technical/private fields and b) this path should not be exercised anymore anyway.290const update = converter.fromOrganizationSettings(request as PlainMessage<OrganizationSettings>);291292await getGitpodService().server.updateOrgSettings(request.organizationId, update);293return new UpdateOrganizationSettingsResponse();294}295296async getOrganizationMaintenanceMode(297request: PartialMessage<GetOrganizationMaintenanceModeRequest>,298options?: CallOptions | undefined,299): Promise<GetOrganizationMaintenanceModeResponse> {300if (!request.organizationId) {301throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");302}303const result = await getGitpodService().server.getTeam(request.organizationId);304return new GetOrganizationMaintenanceModeResponse({305enabled: !!result.maintenanceMode,306});307}308309async setOrganizationMaintenanceMode(310request: PartialMessage<SetOrganizationMaintenanceModeRequest>,311options?: CallOptions | undefined,312): Promise<SetOrganizationMaintenanceModeResponse> {313if (!request.organizationId) {314throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");315}316const result = await getGitpodService().server.updateTeam(request.organizationId, {317maintenanceMode: request.enabled,318});319return new SetOrganizationMaintenanceModeResponse({320enabled: !!result.maintenanceMode,321});322}323324async getMaintenanceNotification(325request: PartialMessage<GetMaintenanceNotificationRequest>,326options?: CallOptions | undefined,327): Promise<GetMaintenanceNotificationResponse> {328if (!request.organizationId) {329throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");330}331const result = await getGitpodService().server.getTeam(request.organizationId);332return new GetMaintenanceNotificationResponse({333isEnabled: result.maintenanceNotification?.enabled || false,334message: result.maintenanceNotification?.message || "",335});336}337338async setMaintenanceNotification(339request: PartialMessage<SetMaintenanceNotificationRequest>,340options?: CallOptions | undefined,341): Promise<SetMaintenanceNotificationResponse> {342if (!request.organizationId) {343throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");344}345const result = await getGitpodService().server.updateTeam(request.organizationId, {346maintenanceNotification: {347enabled: !!request.isEnabled,348message: request.customMessage,349},350});351return new SetMaintenanceNotificationResponse({352isEnabled: result.maintenanceNotification?.enabled || false,353message: result.maintenanceNotification?.message || "",354});355}356}357358359