Skip to content

Commit 2945151

Browse files
authored
Prompt for access level during project creation. (#1029)
1 parent 6e6f80c commit 2945151

File tree

4 files changed

+40
-9
lines changed

4 files changed

+40
-9
lines changed

src/deploy.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ const defaultEffects: DeployEffects = {
6666
};
6767

6868
type DeployTargetInfo =
69-
| {create: true; workspace: {id: string; login: string}; projectSlug: string; title: string}
69+
| {create: true; workspace: {id: string; login: string}; projectSlug: string; title: string; accessLevel: string}
7070
| {create: false; workspace: {id: string; login: string}; project: GetProjectResponse};
7171

7272
/** Deploy a project to ObservableHQ */
@@ -194,7 +194,8 @@ export async function deploy(
194194
const project = await apiClient.postProject({
195195
slug: deployTarget.projectSlug,
196196
title: deployTarget.title,
197-
workspaceId: deployTarget.workspace.id
197+
workspaceId: deployTarget.workspace.id,
198+
accessLevel: deployTarget.accessLevel
198199
});
199200
deployTarget = {create: false, workspace: deployTarget.workspace, project};
200201
} catch (error) {
@@ -492,5 +493,16 @@ export async function promptDeployTarget(
492493
}
493494
projectSlug = projectSlugChoice;
494495

495-
return {create: true, workspace, projectSlug, title};
496+
const accessLevel: string | symbol = await clack.select({
497+
message: "Who is allowed to access your project?",
498+
options: [
499+
{value: "private", label: "Private", hint: "only allow workspace members"},
500+
{value: "public", label: "Public", hint: "allow anyone"}
501+
]
502+
});
503+
if (clack.isCancel(accessLevel)) {
504+
throw new CliError("User canceled deploy.", {print: false, exitCode: 0});
505+
}
506+
507+
return {create: true, workspace, projectSlug, title, accessLevel};
496508
}

src/observableApiClient.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,16 +101,18 @@ export class ObservableApiClient {
101101
async postProject({
102102
title,
103103
slug,
104-
workspaceId
104+
workspaceId,
105+
accessLevel
105106
}: {
106107
title: string;
107108
slug: string;
108109
workspaceId: string;
110+
accessLevel: string;
109111
}): Promise<PostProjectResponse> {
110112
return await this._fetch<PostProjectResponse>(new URL("/cli/project", this._apiOrigin), {
111113
method: "POST",
112114
headers: {"Content-Type": "application/json"},
113-
body: JSON.stringify({title, slug, workspace: workspaceId})
115+
body: JSON.stringify({title, slug, workspace: workspaceId, accessLevel})
114116
});
115117
}
116118

@@ -225,6 +227,7 @@ export interface WorkspaceResponse {
225227
export type PostProjectResponse = GetProjectResponse;
226228

227229
export interface GetProjectResponse {
230+
accessLevel: string;
228231
id: string;
229232
slug: string;
230233
title: string;

test/deploy-test.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ describe("deploy", () => {
213213
.start();
214214

215215
const effects = new MockDeployEffects({deployConfig: DEPLOY_CONFIG, isTty: true});
216-
effects.clack.inputs.push(null, DEPLOY_CONFIG.projectSlug, "fix some bugs");
216+
effects.clack.inputs.push(null, DEPLOY_CONFIG.projectSlug, "private", "fix some bugs");
217217

218218
await deploy(TEST_OPTIONS, effects);
219219

@@ -484,6 +484,7 @@ describe("deploy", () => {
484484
const effects = new MockDeployEffects();
485485
effects.clack.inputs.push(null); // which project do you want to use?
486486
effects.clack.inputs.push("test-project"); // which slug do you want to use?
487+
effects.clack.inputs.push("private"); // who is allowed to access your project?
487488

488489
try {
489490
await deploy(TEST_OPTIONS, effects);
@@ -687,15 +688,18 @@ describe("promptDeployTarget", () => {
687688
const effects = new MockDeployEffects();
688689
const workspace = userWithTwoWorkspaces.workspaces[1];
689690
const projectSlug = "new-project";
691+
const accessLevel = "private";
690692
effects.clack.inputs = [
691693
workspace, // which workspace do you want to use?
692694
true, //
693-
projectSlug // what slug do you want to use
695+
projectSlug, // what slug do you want to use
696+
accessLevel // who is allowed to access your project?
694697
];
695698
const api = new ObservableApiClient({apiKey: {key: validApiKey, source: "test"}});
696699
getCurrentObservableApi().handleGetWorkspaceProjects({workspaceLogin: workspace.login, projects: []}).start();
697700
const result = await promptDeployTarget(effects, api, TEST_CONFIG, userWithTwoWorkspaces);
698701
assert.deepEqual(result, {
702+
accessLevel,
699703
create: true,
700704
projectSlug,
701705
title: "Mock BI",

test/mocks/observableApi.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,17 +95,20 @@ class ObservableApiMock {
9595
projectSlug,
9696
projectId = "project123",
9797
title = "Mock BI",
98+
accessLevel = "private",
9899
status = 200
99100
}: {
100101
workspaceLogin: string;
101102
projectSlug: string;
102103
projectId?: string;
103104
title?: string;
105+
accessLevel?: string;
104106
status?: number;
105107
}): ObservableApiMock {
106108
const response =
107109
status === 200
108110
? JSON.stringify({
111+
accessLevel,
109112
id: projectId,
110113
slug: projectSlug,
111114
title,
@@ -126,12 +129,14 @@ class ObservableApiMock {
126129
projectId = "project123",
127130
workspaceId = workspaces[0].id,
128131
slug = "mock-project",
132+
accessLevel = "private",
129133
status = 200
130134
}: {
131135
projectId?: string;
132136
title?: string;
133137
slug?: string;
134138
workspaceId?: string;
139+
accessLevel?: string;
135140
status?: number;
136141
} = {}): ObservableApiMock {
137142
const owner = workspaces.find((w) => w.id === workspaceId);
@@ -140,6 +145,7 @@ class ObservableApiMock {
140145
const response =
141146
status == 200
142147
? JSON.stringify({
148+
accessLevel,
143149
id: projectId,
144150
slug,
145151
title: "Mock Project",
@@ -181,7 +187,7 @@ class ObservableApiMock {
181187
status = 200
182188
}: {
183189
workspaceLogin: string;
184-
projects: {slug: string; id: string; title?: string}[];
190+
projects: {slug: string; id: string; title?: string; accessLevel?: string}[];
185191
status?: number;
186192
}): ObservableApiMock {
187193
const owner = workspaces.find((w) => w.login === workspaceLogin);
@@ -190,7 +196,13 @@ class ObservableApiMock {
190196
const response =
191197
status === 200
192198
? JSON.stringify({
193-
results: projects.map((p) => ({...p, creator, owner, title: p.title ?? "Mock Title"}))
199+
results: projects.map((p) => ({
200+
...p,
201+
creator,
202+
owner,
203+
title: p.title ?? "Mock Title",
204+
accessLevel: p.accessLevel ?? "private"
205+
}))
194206
} satisfies PaginatedList<GetProjectResponse>)
195207
: emptyErrorBody;
196208
const headers = authorizationHeader(status !== 403);

0 commit comments

Comments
 (0)