Skip to content

Commit aab6e7c

Browse files
n1ru4ljdolle
andauthored
feat: user management public api (#6722)
Co-authored-by: jdolle <[email protected]>
1 parent 20bfc4c commit aab6e7c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+985
-541
lines changed

.changeset/hot-actors-cheer.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
'hive': major
3+
---
4+
5+
Add mutation fields for managing users to the public api schema.
6+
7+
- `Mutation.assignMemberRole`
8+
- `Mutation.createMemberRole`
9+
- `Mutation.deleteMemberRole`
10+
- `Mutation.deleteOrganizationInvitation`
11+
- `Mutation.inviteToOrganizationByEmail`
12+
- `Mutation.updateMemberRole`
13+
14+
**BREAKING CHANGE**: This renames and changes the types for existing types within the private GraphQL schema.

integration-tests/testkit/flow.ts

Lines changed: 44 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,13 @@ export function createOrganization(input: CreateOrganizationInput, authToken: st
8282
}
8383
}
8484
memberRoles {
85-
id
86-
name
87-
locked
85+
edges {
86+
node {
87+
id
88+
name
89+
isLocked
90+
}
91+
}
8892
}
8993
rateLimit {
9094
retentionInDays
@@ -139,11 +143,13 @@ export function inviteToOrganization(input: InviteToOrganizationByEmailInput, au
139143
mutation inviteToOrganization($input: InviteToOrganizationByEmailInput!) {
140144
inviteToOrganizationByEmail(input: $input) {
141145
ok {
142-
id
143-
createdAt
144-
expiresAt
145-
email
146-
code
146+
createdOrganizationInvitation {
147+
id
148+
createdAt
149+
expiresAt
150+
email
151+
code
152+
}
147153
}
148154
error {
149155
message
@@ -230,16 +236,18 @@ export function getOrganizationMembers(selector: OrganizationSelectorInput, auth
230236
query getOrganizationMembers($selector: OrganizationSelectorInput!) {
231237
organization(reference: { bySelector: $selector }) {
232238
members {
233-
nodes {
234-
id
235-
user {
236-
id
237-
email
238-
}
239-
role {
239+
edges {
240+
node {
240241
id
241-
name
242-
permissions
242+
user {
243+
id
244+
email
245+
}
246+
role {
247+
id
248+
name
249+
permissions
250+
}
243251
}
244252
}
245253
}
@@ -675,11 +683,15 @@ export function createMemberRole(input: CreateMemberRoleInput, authToken: string
675683
id
676684
slug
677685
memberRoles {
678-
id
679-
name
680-
description
681-
locked
682-
permissions
686+
edges {
687+
node {
688+
id
689+
name
690+
description
691+
isLocked
692+
permissions
693+
}
694+
}
683695
}
684696
}
685697
}
@@ -733,11 +745,15 @@ export function deleteMemberRole(input: DeleteMemberRoleInput, authToken: string
733745
id
734746
slug
735747
memberRoles {
736-
id
737-
name
738-
description
739-
locked
740-
permissions
748+
edges {
749+
node {
750+
id
751+
name
752+
description
753+
isLocked
754+
permissions
755+
}
756+
}
741757
}
742758
}
743759
}
@@ -764,7 +780,7 @@ export function updateMemberRole(input: UpdateMemberRoleInput, authToken: string
764780
id
765781
name
766782
description
767-
locked
783+
isLocked
768784
permissions
769785
}
770786
}

integration-tests/testkit/seed.ts

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,17 @@ export function initSeed() {
183183
async inviteMember(
184184
email = '[email protected]',
185185
inviteToken = ownerToken,
186-
roleId?: string,
186+
memberRoleId?: string,
187187
) {
188188
const inviteResult = await inviteToOrganization(
189189
{
190+
organization: {
191+
bySelector: {
192+
organizationSlug: organization.slug,
193+
},
194+
},
190195
email,
191-
organizationSlug: organization.slug,
192-
roleId,
196+
memberRoleId,
193197
},
194198
inviteToken,
195199
).then(r => r.expectNoGraphQLErrors());
@@ -205,7 +209,7 @@ export function initSeed() {
205209
ownerToken,
206210
).then(r => r.expectNoGraphQLErrors());
207211

208-
const members = membersResult.organization?.members?.nodes;
212+
const members = membersResult.organization?.members?.edges?.map(edge => edge.node);
209213

210214
if (!members) {
211215
throw new Error(`Could not get members for org ${organization.slug}`);
@@ -848,13 +852,18 @@ export function initSeed() {
848852

849853
const invitationResult = await inviteToOrganization(
850854
{
851-
organizationSlug: organization.slug,
855+
organization: {
856+
bySelector: {
857+
organizationSlug: organization.slug,
858+
},
859+
},
852860
email: memberEmail,
853861
},
854862
inviteToken,
855863
).then(r => r.expectNoGraphQLErrors());
856864

857-
const code = invitationResult.inviteToOrganizationByEmail.ok?.code;
865+
const code =
866+
invitationResult.inviteToOrganizationByEmail.ok?.createdOrganizationInvitation.code;
858867

859868
if (!code) {
860869
throw new Error(
@@ -890,9 +899,17 @@ export function initSeed() {
890899
) {
891900
const memberRoleAssignmentResult = await assignMemberRole(
892901
{
893-
organizationSlug: organization.slug,
894-
userId: input.userId,
895-
roleId: input.roleId,
902+
organization: {
903+
bySelector: {
904+
organizationSlug: organization.slug,
905+
},
906+
},
907+
member: {
908+
byId: input.userId,
909+
},
910+
memberRole: {
911+
byId: input.roleId,
912+
},
896913
resources: input.resources ?? {
897914
mode: GraphQLSchema.ResourceAssignmentModeType.All,
898915
projects: [],
@@ -908,15 +925,16 @@ export function initSeed() {
908925
return memberRoleAssignmentResult.assignMemberRole.ok?.updatedMember;
909926
},
910927
async deleteMemberRole(
911-
roleId: string,
928+
memberRoleId: string,
912929
options: { useMemberToken?: boolean } = {
913930
useMemberToken: false,
914931
},
915932
) {
916933
const memberRoleDeletionResult = await deleteMemberRole(
917934
{
918-
organizationSlug: organization.slug,
919-
roleId,
935+
memberRole: {
936+
byId: memberRoleId,
937+
},
920938
},
921939
options.useMemberToken ? memberToken : ownerToken,
922940
).then(r => r.expectNoGraphQLErrors());
@@ -941,7 +959,11 @@ export function initSeed() {
941959
});
942960
const memberRoleCreationResult = await createMemberRole(
943961
{
944-
organizationSlug: organization.slug,
962+
organization: {
963+
bySelector: {
964+
organizationSlug: organization.slug,
965+
},
966+
},
945967
name,
946968
description: 'some description',
947969
selectedPermissions: permissions,
@@ -965,9 +987,9 @@ export function initSeed() {
965987
}
966988

967989
const createdRole =
968-
memberRoleCreationResult.createMemberRole.ok?.updatedOrganization.memberRoles?.find(
969-
r => r.name === name,
970-
);
990+
memberRoleCreationResult.createMemberRole.ok?.updatedOrganization.memberRoles?.edges.find(
991+
e => e.node.name === name,
992+
)?.node;
971993

972994
if (!createdRole) {
973995
throw new Error(
@@ -990,8 +1012,9 @@ export function initSeed() {
9901012
) {
9911013
const memberRoleUpdateResult = await updateMemberRole(
9921014
{
993-
organizationSlug: organization.slug,
994-
roleId: role.id,
1015+
memberRole: {
1016+
byId: role.id,
1017+
},
9951018
name: role.name,
9961019
description: role.description,
9971020
selectedPermissions: permissions,

integration-tests/tests/api/oidc-integrations/crud.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -798,7 +798,7 @@ describe('restrictions', () => {
798798
expect(refetchedOrg.organization?.oidcIntegration?.oidcUserAccessOnly).toEqual(true);
799799

800800
const invitation = await inviteMember('[email protected]');
801-
const invitationCode = invitation.ok?.code;
801+
const invitationCode = invitation.ok?.createdOrganizationInvitation.code;
802802

803803
if (!invitationCode) {
804804
throw new Error('No invitation code');
@@ -865,7 +865,7 @@ describe('restrictions', () => {
865865
).toEqual(false);
866866

867867
const invitation = await inviteMember('[email protected]');
868-
const invitationCode = invitation.ok?.code;
868+
const invitationCode = invitation.ok?.createdOrganizationInvitation.code;
869869

870870
if (!invitationCode) {
871871
throw new Error('No invitation code');
@@ -887,7 +887,7 @@ describe('restrictions', () => {
887887
const { organization, inviteMember, joinMemberUsingCode } = await createOrg();
888888

889889
const invitation = await inviteMember('[email protected]');
890-
const invitationCode = invitation.ok?.code;
890+
const invitationCode = invitation.ok?.createdOrganizationInvitation.code;
891891

892892
if (!invitationCode) {
893893
throw new Error('No invitation code');

integration-tests/tests/api/organization/members.spec.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ test.concurrent('email invitation', async ({ expect }) => {
104104

105105
const inviteEmail = seed.generateEmail();
106106
const invitationResult = await inviteMember(inviteEmail);
107-
const inviteCode = invitationResult.ok?.code;
107+
const inviteCode = invitationResult.ok?.createdOrganizationInvitation.code;
108108
expect(inviteCode).toBeDefined();
109109

110110
const sentEmails = await history();
@@ -120,7 +120,7 @@ test.concurrent(
120120

121121
// Invite
122122
const invitationResult = await inviteMember();
123-
const inviteCode = invitationResult.ok!.code;
123+
const inviteCode = invitationResult.ok!.createdOrganizationInvitation.code;
124124
expect(inviteCode).toBeDefined();
125125

126126
// Join
@@ -150,9 +150,10 @@ const OrganizationInvitationsQuery = graphql(`
150150
organization: organizationBySlug(organizationSlug: $organizationSlug) {
151151
id
152152
invitations {
153-
total
154-
nodes {
155-
id
153+
edges {
154+
node {
155+
id
156+
}
156157
}
157158
}
158159
}

integration-tests/tests/api/policy/policy-access.spec.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ describe('Policy Access', () => {
5454
const { createOrg } = await initSeed().createOwner();
5555
const { organization, createProject, inviteAndJoinMember } = await createOrg();
5656
const { project } = await createProject(ProjectType.Single);
57-
const adminRole = organization.memberRoles?.find(r => r.name === 'Admin');
57+
const adminRole = organization.memberRoles?.edges.find(e => e.node.name === 'Admin')?.node;
5858

5959
if (!adminRole) {
6060
throw new Error('Admin role not found');
@@ -183,7 +183,9 @@ describe('Policy Access', () => {
183183
async ({ expect }) => {
184184
const { createOrg } = await initSeed().createOwner();
185185
const { organization, inviteAndJoinMember } = await createOrg();
186-
const adminRole = organization.memberRoles?.find(r => r.name === 'Admin');
186+
const adminRole = organization.memberRoles?.edges.find(
187+
edge => edge.node.name === 'Admin',
188+
)?.node;
187189

188190
if (!adminRole) {
189191
throw new Error('Admin role not found');

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,15 @@ test.concurrent('organization member user can create a target', async ({ expect
168168
const inviteMemberResult = await inviteMember(
169169
orgMemberEmail,
170170
undefined,
171-
organization.memberRoles?.find(role => role.name === 'Admin')?.id,
171+
organization.memberRoles?.edges?.find(edge => edge.node.name === 'Admin')?.node.id,
172172
);
173173

174174
if (inviteMemberResult.ok == null) {
175175
throw new Error('Invite did not succeed' + JSON.stringify(inviteMemberResult));
176176
}
177177

178178
const joinMemberUsingCodeResult = await joinMemberUsingCode(
179-
inviteMemberResult.ok.code,
179+
inviteMemberResult.ok.createdOrganizationInvitation.code,
180180
orgMemberToken,
181181
).then(r => r.expectNoGraphQLErrors());
182182

0 commit comments

Comments
 (0)