Skip to content

Commit 24f38b4

Browse files
committed
[api, server, dashboard] Cleanup UpdateOrganizationSettings API
Tool: gitpod/catfood.gitpod.cloud
1 parent 04f590d commit 24f38b4

File tree

13 files changed

+3085
-5120
lines changed

13 files changed

+3085
-5120
lines changed

components/dashboard/src/data/organizations/update-org-settings-mutation.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@ import { useMutation } from "@tanstack/react-query";
88
import { useOrgSettingsQueryInvalidator } from "./org-settings-query";
99
import { useCurrentOrg } from "./orgs-query";
1010
import { organizationClient } from "../../service/public-api";
11-
import {
12-
OrganizationSettings,
13-
UpdateOrganizationSettingsRequest,
14-
} from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
11+
import { OrganizationSettings } from "@gitpod/public-api/lib/gitpod/v1/organization_pb";
1512
import { ErrorCode } from "@gitpod/gitpod-protocol/lib/messaging/error";
1613
import { useOrgWorkspaceClassesQueryInvalidator } from "./org-workspace-classes-query";
1714
import { useOrgRepoSuggestionsInvalidator } from "./suggested-repositories-query";
1815
import { PartialMessage } from "@bufbuild/protobuf";
1916

20-
export type UpdateOrganizationSettingsArgs = PartialMessage<UpdateOrganizationSettingsRequest>;
17+
export type UpdateOrganizationSettingsArgs = PartialMessage<OrganizationSettings>;
2118

2219
export const useUpdateOrgSettingsMutation = () => {
2320
const org = useCurrentOrg().data;
@@ -28,10 +25,9 @@ export const useUpdateOrgSettingsMutation = () => {
2825

2926
return useMutation<OrganizationSettings, Error, UpdateOrganizationSettingsArgs>({
3027
mutationFn: async (partialUpdate) => {
31-
const update: PartialMessage<UpdateOrganizationSettingsRequest> = {
28+
const update: UpdateOrganizationSettingsArgs = {
3229
...partialUpdate,
3330
};
34-
update.organizationId = organizationId;
3531
update.updatePinnedEditorVersions = update.pinnedEditorVersions !== undefined;
3632
update.updateRestrictedEditorNames = update.restrictedEditorNames !== undefined;
3733
update.updateRoleRestrictions = update.roleRestrictions !== undefined;
@@ -44,7 +40,10 @@ export const useUpdateOrgSettingsMutation = () => {
4440
}
4541
}
4642

47-
const settings = await organizationClient.updateOrganizationSettings(update);
43+
const settings = await organizationClient.updateOrganizationSettings({
44+
organizationId: organizationId,
45+
settings: update,
46+
});
4847
return settings.settings!;
4948
},
5049
onSuccess: () => {

components/dashboard/src/service/json-rpc-organization-client.ts

Lines changed: 43 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* See License.AGPL.txt in the project root for license information.
55
*/
66

7-
import { PartialMessage } from "@bufbuild/protobuf";
7+
import { PartialMessage, PlainMessage } from "@bufbuild/protobuf";
88
import { CallOptions, PromiseClient } from "@connectrpc/connect";
99
import { OrganizationService } from "@gitpod/public-api/lib/gitpod/v1/organization_connect";
1010
import {
@@ -41,7 +41,6 @@ import {
4141
import { getGitpodService } from "./service";
4242
import { converter } from "./public-api";
4343
import { ApplicationError, ErrorCodes } from "@gitpod/gitpod-protocol/lib/messaging/error";
44-
import { OrgMemberRole, RoleRestrictions } from "@gitpod/gitpod-protocol";
4544

4645
export class JsonRpcOrganizationClient implements PromiseClient<typeof OrganizationService> {
4746
async createOrganization(
@@ -228,56 +227,62 @@ export class JsonRpcOrganizationClient implements PromiseClient<typeof Organizat
228227
if (!request.organizationId) {
229228
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "organizationId is required");
230229
}
231-
const update: Partial<OrganizationSettings> = {
232-
workspaceSharingDisabled: request?.workspaceSharingDisabled,
233-
defaultWorkspaceImage: request?.defaultWorkspaceImage,
234-
allowedWorkspaceClasses: request?.allowedWorkspaceClasses,
235-
restrictedEditorNames: request?.restrictedEditorNames,
236-
defaultRole: request?.defaultRole,
237-
};
238-
if (request.updatePinnedEditorVersions) {
239-
update.pinnedEditorVersions = request.pinnedEditorVersions;
240-
} else if (request.pinnedEditorVersions && Object.keys(request.pinnedEditorVersions).length > 0) {
230+
const settings = request.settings;
231+
if (!settings) {
232+
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "nothing to update");
233+
}
234+
235+
if (
236+
settings.restrictedEditorNames &&
237+
settings.restrictedEditorNames.length > 0 &&
238+
!settings.updateRestrictedEditorNames
239+
) {
241240
throw new ApplicationError(
242241
ErrorCodes.BAD_REQUEST,
243-
"updatePinnedEditorVersions is required to be true to update pinnedEditorVersions",
242+
"updateRestrictedEditorNames is required to be true to update restrictedEditorNames",
244243
);
245244
}
246-
if (request.updateRestrictedEditorNames) {
247-
update.restrictedEditorNames = request.restrictedEditorNames;
248-
} else if (request.restrictedEditorNames && request.restrictedEditorNames.length > 0) {
245+
246+
if (
247+
settings.allowedWorkspaceClasses &&
248+
settings.allowedWorkspaceClasses.length > 0 &&
249+
!settings.updateAllowedWorkspaceClasses
250+
) {
249251
throw new ApplicationError(
250252
ErrorCodes.BAD_REQUEST,
251-
"updateRestrictedEditorNames is required to be true to update restrictedEditorNames",
253+
"updateAllowedWorkspaceClasses is required to be true to update allowedWorkspaceClasses",
252254
);
253255
}
254-
const roleRestrictions: RoleRestrictions = {};
255-
if (request.updateRoleRestrictions) {
256-
for (const roleRestriction of request?.roleRestrictions ?? []) {
257-
if (!roleRestriction.role) {
258-
throw new ApplicationError(ErrorCodes.BAD_REQUEST, "role is required");
259-
}
260-
const role = converter.fromOrgMemberRole(roleRestriction.role);
261-
const permissions = roleRestriction?.permissions?.map((p) => converter.fromOrganizationPermission(p));
262256

263-
roleRestrictions[role] = permissions;
264-
}
265-
} else if (request.roleRestrictions && Object.keys(request.roleRestrictions).length > 0) {
257+
if (settings.pinnedEditorVersions && !settings.updatePinnedEditorVersions) {
266258
throw new ApplicationError(
267259
ErrorCodes.BAD_REQUEST,
268-
"updateRoleRestrictions is required to be true to update roleRestrictions",
260+
"updatePinnedEditorVersions is required to be true to update pinnedEditorVersions",
269261
);
270262
}
271263

272-
await getGitpodService().server.updateOrgSettings(request.organizationId, {
273-
...update,
274-
defaultRole: request.defaultRole as OrgMemberRole,
275-
timeoutSettings: {
276-
inactivity: converter.toDurationStringOpt(request.timeoutSettings?.inactivity),
277-
denyUserTimeouts: request.timeoutSettings?.denyUserTimeouts,
278-
},
279-
roleRestrictions,
280-
});
264+
if (settings.roleRestrictions && settings.roleRestrictions.length > 0 && !settings.updateRoleRestrictions) {
265+
throw new ApplicationError(
266+
ErrorCodes.BAD_REQUEST,
267+
"updateRoleRestrictions is required to be true when updating roleRestrictions",
268+
);
269+
}
270+
if (
271+
settings.onboardingSettings?.recommendedRepositories &&
272+
settings.onboardingSettings.recommendedRepositories.length > 0 &&
273+
!settings.onboardingSettings.updateRecommendedRepositories
274+
) {
275+
throw new ApplicationError(
276+
ErrorCodes.BAD_REQUEST,
277+
"recommendedRepositories can only be set when updateRecommendedRepositories is true",
278+
);
279+
}
280+
281+
// gpl: We accept the little bit of uncertainty here because a) the partial/not-partial mismatch is only about
282+
// technical/private fields and b) this path should not be exercised anymore anyway.
283+
const update = converter.fromOrganizationSettings(settings as PlainMessage<OrganizationSettings>);
284+
285+
await getGitpodService().server.updateOrgSettings(request.organizationId, update);
281286
return new UpdateOrganizationSettingsResponse();
282287
}
283288
}

components/dashboard/src/teams/TeamOnboarding.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ export default function TeamOnboardingPage() {
5454
}
5555
try {
5656
await updateTeamSettings.mutateAsync({
57-
...settings,
5857
...newSettings,
5958
});
6059
toast("Organization settings updated");
@@ -66,7 +65,7 @@ export default function TeamOnboardingPage() {
6665
console.error(error);
6766
}
6867
},
69-
[updateTeamSettings, org?.id, isOwner, settings, toast],
68+
[updateTeamSettings, org?.id, isOwner, toast],
7069
);
7170

7271
const handleUpdateInternalLink = useCallback(

components/gitpod-db/src/typeorm/entity/db-team-settings.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,34 +25,34 @@ export class DBOrgSettings implements OrganizationSettings {
2525
workspaceSharingDisabled?: boolean;
2626

2727
@Column("varchar", { nullable: true })
28-
defaultWorkspaceImage?: string | null;
28+
defaultWorkspaceImage?: string;
2929

3030
@Column("json", { nullable: true })
31-
allowedWorkspaceClasses?: string[] | null;
31+
allowedWorkspaceClasses?: string[];
3232

3333
@Column("json", { nullable: true })
34-
pinnedEditorVersions?: { [key: string]: string } | null;
34+
pinnedEditorVersions?: { [key: string]: string };
3535

3636
@Column("json", { nullable: true })
37-
restrictedEditorNames?: string[] | null;
37+
restrictedEditorNames?: string[];
3838

3939
@Column("varchar", { nullable: true })
40-
defaultRole?: OrgMemberRole | undefined;
40+
defaultRole?: OrgMemberRole;
4141

4242
@Column("json", { nullable: true })
43-
timeoutSettings?: TimeoutSettings | undefined;
43+
timeoutSettings?: TimeoutSettings;
4444

4545
@Column("json", { nullable: true })
46-
roleRestrictions?: RoleRestrictions | undefined;
46+
roleRestrictions?: RoleRestrictions;
4747

4848
@Column({ type: "int", default: 0 })
4949
maxParallelRunningWorkspaces: number;
5050

5151
@Column("json", { nullable: true })
52-
onboardingSettings?: OnboardingSettings | undefined;
52+
onboardingSettings?: OnboardingSettings;
5353

5454
@Column({ type: "boolean", default: false })
55-
annotateGitCommits?: boolean | undefined;
55+
annotateGitCommits?: boolean;
5656

5757
@Column()
5858
deleted: boolean;

components/gitpod-protocol/src/teams-projects-protocol.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -221,15 +221,15 @@ export interface Organization {
221221

222222
export interface OrganizationSettings {
223223
workspaceSharingDisabled?: boolean;
224-
// null or empty string to reset to default
225-
defaultWorkspaceImage?: string | null;
224+
// undefined or empty string to reset to default
225+
defaultWorkspaceImage?: string;
226226

227227
// empty array to allow all kind of workspace classes
228-
allowedWorkspaceClasses?: string[] | null;
228+
allowedWorkspaceClasses?: string[];
229229

230-
pinnedEditorVersions?: { [key: string]: string } | null;
230+
pinnedEditorVersions?: { [key: string]: string };
231231

232-
restrictedEditorNames?: string[] | null;
232+
restrictedEditorNames?: string[];
233233

234234
// what role new members will get, default is "member"
235235
defaultRole?: OrgMemberRole;

components/public-api/gitpod/v1/organization.proto

Lines changed: 52 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -86,20 +86,62 @@ message OnboardingSettings {
8686
}
8787

8888
message OrganizationSettings {
89-
bool workspace_sharing_disabled = 1;
90-
string default_workspace_image = 2;
89+
optional bool workspace_sharing_disabled = 1;
90+
91+
// pass empty string to reset to the installation default workspace image
92+
optional string default_workspace_image = 2;
93+
94+
// allowed_workspace_classes are the IDs of classes, which can be used by
95+
// workspaces in an organization. Pass an empty array to allow all workspace
96+
// classes.
97+
// Only updates if update_allowed_workspace_classes is true.
9198
repeated string allowed_workspace_classes = 3;
99+
100+
// restricted_editor_names updates the list of restricted editor names that
101+
// are not allowed to be used by workspaces in an organization. If empty, all
102+
// editors are allowed.
103+
// Only updates if update_restricted_editor_names is true.
92104
repeated string restricted_editor_names = 4;
105+
106+
// pinned_editor_versions updates the pinned version for the corresponding
107+
// editor.
108+
// Only updates if update_pinned_editor_versions is true.
93109
map<string, string> pinned_editor_versions = 5;
94-
string default_role = 6;
95-
TimeoutSettings timeout_settings = 7;
110+
111+
// default_role is the default role for new members in the organization
112+
optional string default_role = 6;
113+
114+
// timeout_settings are the settings for workspace timeouts
115+
optional TimeoutSettings timeout_settings = 7;
116+
117+
// Only updates if update_role_restrictions is true.
96118
repeated RoleRestrictionEntry role_restrictions = 8;
119+
97120
// max_parallel_running_workspaces is the maximum number of workspaces that a
98121
// single user can run in parallel. 0 resets to the default, which depends on
99122
// the org plan
100-
int32 max_parallel_running_workspaces = 9;
101-
OnboardingSettings onboarding_settings = 10;
102-
bool annotate_git_commits = 11;
123+
optional int32 max_parallel_running_workspaces = 9;
124+
125+
// onboarding_settings are the settings for the organization's onboarding
126+
optional OnboardingSettings onboarding_settings = 10;
127+
128+
// annotate_git_commits specifies whether to annotate git commits created in
129+
// Gitpod workspaces with the gitpod host
130+
optional bool annotate_git_commits = 11;
131+
132+
// update_role_restrictions specifies whether role_restrictions should be
133+
// updated
134+
optional bool update_allowed_workspace_classes = 12;
135+
136+
// Specifies whether restricted_workspace_classes should be updated
137+
optional bool update_restricted_editor_names = 13;
138+
139+
// Specifies whether pinned_editor_versions should be updated
140+
optional bool update_pinned_editor_versions = 14;
141+
142+
// update_role_restrictions specifies whether role_restrictions should be
143+
// updated
144+
optional bool update_role_restrictions = 15;
103145
}
104146

105147
service OrganizationService {
@@ -187,62 +229,10 @@ message UpdateOrganizationSettingsRequest {
187229
// organization_id is the ID of the organization to update the settings for
188230
string organization_id = 1;
189231

190-
optional bool workspace_sharing_disabled = 3;
191-
192-
// pass empty string to reset to the installation default workspace image
193-
optional string default_workspace_image = 4;
194-
195-
// allowed_workspace_classes are the IDs of classes, which can be used by
196-
// workspaces in an organization. Pass an empty array to allow all workspace
197-
// classes.
198-
// Only updates if update_allowed_workspace_classes is true.
199-
repeated string allowed_workspace_classes = 5;
200-
201-
// restricted_editor_names updates the list of restricted editor names that
202-
// are not allowed to be used by workspaces in an organization. If empty, all
203-
// editors are allowed.
204-
// Only updates if update_restricted_editor_names is true.
205-
repeated string restricted_editor_names = 6;
206-
207-
// Specifies whether restricted_workspace_classes should be updated
208-
optional bool update_restricted_editor_names = 7;
209-
210-
// pinned_editor_versions updates the pinned version for the corresponding
211-
// editor.
212-
// Only updates if update_pinned_editor_versions is true.
213-
map<string, string> pinned_editor_versions = 8;
232+
// fields 2-28 were prior used for what's now combined in OrganizationSettings
214233

215-
// Specifies whether pinned_editor_versions should be updated
216-
optional bool update_pinned_editor_versions = 9;
217-
218-
// default_role is the default role for new members in the organization
219-
optional string default_role = 10;
220-
221-
// timeout_settings are the settings for workspace timeouts
222-
optional TimeoutSettings timeout_settings = 11;
223-
224-
// Only updates if update_role_restrictions is true.
225-
repeated RoleRestrictionEntry role_restrictions = 12;
226-
227-
// update_role_restrictions specifies whether role_restrictions should be
228-
// updated
229-
optional bool update_role_restrictions = 13;
230-
231-
// max_parallel_running_workspaces is the maximum number of workspaces that a
232-
// single user can run in parallel. 0 resets to the default, which depends on
233-
// the org plan
234-
optional int32 max_parallel_running_workspaces = 15;
235-
236-
// onboarding_settings are the settings for the organization's onboarding
237-
optional OnboardingSettings onboarding_settings = 16;
238-
239-
// annotate_git_commits specifies whether to annotate git commits created in
240-
// Gitpod workspaces with the gitpod host
241-
optional bool annotate_git_commits = 17;
242-
243-
// update_role_restrictions specifies whether role_restrictions should be
244-
// updated
245-
optional bool update_allowed_workspace_classes = 18;
234+
// settings to persist
235+
OrganizationSettings settings = 19;
246236
}
247237

248238
message UpdateOrganizationSettingsResponse {

0 commit comments

Comments
 (0)