Skip to content

Commit 20bfc4c

Browse files
authored
feat(api): add project mutations to public api schema (#6786)
1 parent bbd5643 commit 20bfc4c

File tree

14 files changed

+281
-239
lines changed

14 files changed

+281
-239
lines changed

.changeset/lemon-lies-carry.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
'hive': major
3+
---
4+
5+
Add mutation fields for managing projects to the public api schema.
6+
- `Mutation.createProject`
7+
- `Mutation.updateProjectSlug`
8+
- `Mutation.deleteProject`
9+
10+
**BREAKING CHANGE**: This renames and changes the types for existing types within the private GraphQL schema.

integration-tests/testkit/flow.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,7 @@ export function updateProjectSlug(input: UpdateProjectSlugInput, authToken: stri
378378
mutation updateProjectSlug($input: UpdateProjectSlugInput!) {
379379
updateProjectSlug(input: $input) {
380380
ok {
381-
selector {
382-
organizationSlug
383-
projectSlug
384-
}
385-
project {
381+
updatedProject {
386382
id
387383
name
388384
slug

integration-tests/testkit/seed.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,11 @@ export function initSeed() {
230230
async createProject(projectType: ProjectType = ProjectType.Single) {
231231
const projectResult = await createProject(
232232
{
233-
organizationSlug: organization.slug,
233+
organization: {
234+
bySelector: {
235+
organizationSlug: organization.slug,
236+
},
237+
},
234238
type: projectType,
235239
slug: generateUnique(),
236240
},

integration-tests/tests/api/project/crud.spec.ts

Lines changed: 50 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,20 @@ test.concurrent(`changing a project's slug should result changing its name`, asy
3838

3939
const renameResult = await updateProjectSlug(
4040
{
41-
organizationSlug: organization.slug,
42-
projectSlug: project.slug,
41+
project: {
42+
bySelector: {
43+
organizationSlug: organization.slug,
44+
projectSlug: project.slug,
45+
},
46+
},
4347
slug: 'bar',
4448
},
4549
ownerToken,
4650
).then(r => r.expectNoGraphQLErrors());
4751

4852
expect(renameResult.updateProjectSlug.error).toBeNull();
49-
expect(renameResult.updateProjectSlug.ok?.project.name).toBe('bar');
50-
expect(renameResult.updateProjectSlug.ok?.project.slug).toBe('bar');
51-
expect(renameResult.updateProjectSlug.ok?.selector.projectSlug).toBe('bar');
53+
expect(renameResult.updateProjectSlug.ok?.updatedProject.name).toBe('bar');
54+
expect(renameResult.updateProjectSlug.ok?.updatedProject.slug).toBe('bar');
5255
});
5356

5457
test.concurrent(
@@ -60,17 +63,20 @@ test.concurrent(
6063

6164
const renameResult = await updateProjectSlug(
6265
{
63-
organizationSlug: organization.slug,
64-
projectSlug: project.slug,
66+
project: {
67+
bySelector: {
68+
organizationSlug: organization.slug,
69+
projectSlug: project.slug,
70+
},
71+
},
6572
slug: project.slug,
6673
},
6774
ownerToken,
6875
).then(r => r.expectNoGraphQLErrors());
6976

7077
expect(renameResult.updateProjectSlug.error).toBeNull();
71-
expect(renameResult.updateProjectSlug.ok?.project.name).toBe(project.slug);
72-
expect(renameResult.updateProjectSlug.ok?.project.slug).toBe(project.slug);
73-
expect(renameResult.updateProjectSlug.ok?.selector.projectSlug).toBe(project.slug);
78+
expect(renameResult.updateProjectSlug.ok?.updatedProject.name).toBe(project.slug);
79+
expect(renameResult.updateProjectSlug.ok?.updatedProject.slug).toBe(project.slug);
7480
},
7581
);
7682

@@ -84,8 +90,12 @@ test.concurrent(
8490

8591
const renameResult = await updateProjectSlug(
8692
{
87-
organizationSlug: organization.slug,
88-
projectSlug: project.slug,
93+
project: {
94+
bySelector: {
95+
organizationSlug: organization.slug,
96+
projectSlug: project.slug,
97+
},
98+
},
8999
slug: project2.slug,
90100
},
91101
ownerToken,
@@ -116,24 +126,27 @@ test.concurrent(
116126
`changing a project's slug to a slug taken by another organization should be possible`,
117127
async ({ expect }) => {
118128
const { createOrg, ownerToken } = await initSeed().createOwner();
119-
const { createProject, organization, projects } = await createOrg();
120-
const { createProject: createProject2, organization: organization2 } = await createOrg();
129+
const { createProject, organization } = await createOrg();
130+
const { createProject: createProject2 } = await createOrg();
121131
const { project } = await createProject(ProjectType.Single);
122132
const { project: project2 } = await createProject2(ProjectType.Single);
123133

124134
const renameResult = await updateProjectSlug(
125135
{
126-
organizationSlug: organization.slug,
127-
projectSlug: project.slug,
136+
project: {
137+
bySelector: {
138+
organizationSlug: organization.slug,
139+
projectSlug: project.slug,
140+
},
141+
},
128142
slug: project2.slug,
129143
},
130144
ownerToken,
131145
).then(r => r.expectNoGraphQLErrors());
132146

133147
expect(renameResult.updateProjectSlug.error).toBeNull();
134-
expect(renameResult.updateProjectSlug.ok?.project.name).toBe(project2.slug);
135-
expect(renameResult.updateProjectSlug.ok?.project.slug).toBe(project2.slug);
136-
expect(renameResult.updateProjectSlug.ok?.selector.projectSlug).toBe(project2.slug);
148+
expect(renameResult.updateProjectSlug.ok?.updatedProject.name).toBe(project2.slug);
149+
expect(renameResult.updateProjectSlug.ok?.updatedProject.slug).toBe(project2.slug);
137150
},
138151
);
139152

@@ -146,8 +159,12 @@ test.concurrent(
146159

147160
const renameResult = await updateProjectSlug(
148161
{
149-
organizationSlug: organization.slug,
150-
projectSlug: project.slug,
162+
project: {
163+
bySelector: {
164+
organizationSlug: organization.slug,
165+
projectSlug: project.slug,
166+
},
167+
},
151168
slug: 'view',
152169
},
153170
ownerToken,
@@ -167,8 +184,12 @@ test.concurrent(
167184

168185
const renameResult = await updateProjectSlug(
169186
{
170-
organizationSlug: organization.slug,
171-
projectSlug: project.slug,
187+
project: {
188+
bySelector: {
189+
organizationSlug: organization.slug,
190+
projectSlug: project.slug,
191+
},
192+
},
172193
slug: 'new',
173194
},
174195
ownerToken,
@@ -188,8 +209,12 @@ test.concurrent(
188209

189210
const renameResult = await updateProjectSlug(
190211
{
191-
organizationSlug: organization.slug,
192-
projectSlug: project.slug,
212+
project: {
213+
bySelector: {
214+
organizationSlug: organization.slug,
215+
projectSlug: project.slug,
216+
},
217+
},
193218
slug: 'new',
194219
},
195220
ownerToken,

packages/services/api/src/modules/organization/lib/organization-access-token-permissions.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,18 @@ export const permissionGroups: Array<PermissionGroup> = [
3232
title: 'Describe project',
3333
description: 'Fetch information about the specified projects.',
3434
},
35+
{
36+
id: 'project:delete',
37+
title: 'Delete project',
38+
description: 'Delete projects.',
39+
dependsOn: 'project:describe',
40+
},
41+
{
42+
id: 'project:modifySettings',
43+
title: 'Modify Settings',
44+
description: 'Modify the project settings.',
45+
dependsOn: 'project:describe',
46+
},
3547
],
3648
},
3749
{

packages/services/api/src/modules/project/module.graphql.ts

Lines changed: 41 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ export default gql`
77
}
88
99
extend type Mutation {
10-
createProject(input: CreateProjectInput!): CreateProjectResult!
11-
updateProjectSlug(input: UpdateProjectSlugInput!): UpdateProjectSlugResult!
12-
deleteProject(selector: ProjectSelectorInput!): DeleteProjectPayload!
10+
createProject(input: CreateProjectInput! @tag(name: "public")): CreateProjectResult!
11+
@tag(name: "public")
12+
updateProjectSlug(
13+
input: UpdateProjectSlugInput! @tag(name: "public")
14+
): UpdateProjectSlugResult! @tag(name: "public")
15+
deleteProject(input: DeleteProjectInput! @tag(name: "public")): DeleteProjectResult!
16+
@tag(name: "public")
1317
}
1418
1519
type UpdateProjectGitRepositoryResult {
@@ -27,42 +31,41 @@ export default gql`
2731
}
2832
2933
input UpdateProjectSlugInput {
30-
slug: String!
31-
organizationSlug: String!
32-
projectSlug: String!
34+
project: ProjectReferenceInput! @tag(name: "public")
35+
slug: String! @tag(name: "public")
3336
}
3437
3538
type UpdateProjectSlugResult {
36-
ok: UpdateProjectSlugOk
37-
error: UpdateProjectSlugError
39+
ok: UpdateProjectSlugOk @tag(name: "public")
40+
error: UpdateProjectSlugError @tag(name: "public")
3841
}
3942
4043
type UpdateProjectSlugOk {
41-
selector: ProjectSelector!
42-
project: Project!
44+
updatedProject: Project! @tag(name: "public")
4345
}
4446
45-
type UpdateProjectSlugError implements Error {
46-
message: String!
47+
type UpdateProjectSlugError {
48+
message: String! @tag(name: "public")
4749
}
4850
4951
type CreateProjectResult {
50-
ok: CreateProjectOk
51-
error: CreateProjectError
52+
ok: CreateProjectResultOk @tag(name: "public")
53+
error: CreateProjectResultError @tag(name: "public")
5254
}
53-
type CreateProjectOk {
54-
createdProject: Project!
55+
56+
type CreateProjectResultOk {
57+
createdProject: Project! @tag(name: "public")
5558
createdTargets: [Target!]!
5659
updatedOrganization: Organization!
5760
}
5861
5962
type CreateProjectInputErrors {
60-
slug: String
63+
slug: String @tag(name: "public")
6164
}
6265
63-
type CreateProjectError implements Error {
64-
message: String!
65-
inputErrors: CreateProjectInputErrors!
66+
type CreateProjectResultError {
67+
message: String! @tag(name: "public")
68+
inputErrors: CreateProjectInputErrors! @tag(name: "public")
6669
}
6770
6871
input ProjectSelectorInput {
@@ -125,9 +128,9 @@ export default gql`
125128
}
126129
127130
input CreateProjectInput {
128-
slug: String!
129-
type: ProjectType!
130-
organizationSlug: String!
131+
organization: OrganizationReferenceInput! @tag(name: "public")
132+
slug: String! @tag(name: "public")
133+
type: ProjectType! @tag(name: "public")
131134
}
132135
133136
input UpdateProjectGitRepositoryInput {
@@ -141,8 +144,20 @@ export default gql`
141144
updatedProject: Project!
142145
}
143146
144-
type DeleteProjectPayload {
145-
selector: ProjectSelector!
146-
deletedProject: Project!
147+
input DeleteProjectInput {
148+
project: ProjectReferenceInput! @tag(name: "public")
149+
}
150+
151+
type DeleteProjectResult {
152+
ok: DeleteProjectResultOk @tag(name: "public")
153+
error: DeleteProjectResultError @tag(name: "public")
154+
}
155+
156+
type DeleteProjectResultOk {
157+
deletedProjectId: ID! @tag(name: "public")
158+
}
159+
160+
type DeleteProjectResultError {
161+
message: String! @tag(name: "public")
147162
}
148163
`;

0 commit comments

Comments
 (0)