Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/sweet-stamps-watch.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@itwin/access-control-client": minor
---

Added new endpoint for accepting user invites
7 changes: 7 additions & 0 deletions src/accessControlClientInterfaces/MemberInvitationsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,11 @@ export interface IMemberInvitationsClient {
iTwinId: string,
invitationId: string
): Promise<BentleyAPIResponse<undefined>>;

/** Accepts an existing member invitation. */
acceptITwinMemberInvitation(
accessToken: AccessToken,
iTwinId: string,
invitationId: string
): Promise<BentleyAPIResponse<undefined>>;
}
18 changes: 18 additions & 0 deletions src/subClients/MemberInvitationsClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,24 @@ export class MemberInvitationsClient
return this.sendGenericAPIRequest(accessToken, "DELETE", url);
}

/** Accepts a member invitation.
* @param accessToken The client access token string
* @param iTwinId The id of the iTwin
* @param invitationId The id of the invitation id
* @returns No Content
* @beta
* @remarks On success, the invited user is added as an iTwin member with the roles from all pending invitations for that user. After acceptance, all of that user’s pending invitations for the iTwin are consumed and can no longer be accessed.
* If the invited user is already a member of the iTwin, the request is treated as a success. Only the invited user can accept this invitation through this API. The invited user must already exist as a member in IMS.
*/
public async acceptITwinMemberInvitation(
accessToken: AccessToken,
iTwinId: string,
invitationId: string
): Promise<BentleyAPIResponse<undefined>> {
const url = `${this._baseUrl}/${iTwinId}/members/invitations/${invitationId}/accept`;
return this.sendGenericAPIRequest(accessToken, "POST", url);
}

/** Deletes a member invitations.
* @param accessToken The client access token string
* @param iTwinId The id of the iTwin
Expand Down
24 changes: 24 additions & 0 deletions src/test/integration/accessControlClientMemberInvitations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import type { AccessToken } from "@itwin/core-bentley";
import { randomUUID } from "node:crypto";
import { beforeAll, describe, expect, it } from "vitest";
import { AccessControlClient } from "../../AccessControlClient";
import type {
Expand Down Expand Up @@ -215,4 +216,27 @@ describe("AccessControlClient Member Invitations", () => {
expect(memberInvitation.error!.message).toBe("Requested iTwin is not available.");
expect(memberInvitation.error!.code).toBe("ItwinNotFound");
});

it("should return 204 when accepting a random invitation GUID if the user is already a member", async () => {
const invitationId = randomUUID();
const response = await accessControlClient.memberInvitations.acceptITwinMemberInvitation(
accessToken,
TestConfig.itwinId,
invitationId,
);

expect(response.status).toBe(204);
});

it("accept member invitation fake itwin id, 404", async () => {
const fakeITwinId = randomUUID();
const invitationId = randomUUID();
const response = await accessControlClient.memberInvitations.acceptITwinMemberInvitation(
accessToken,
fakeITwinId,
invitationId,
);

expect(response.status).toBe(404);
});
});
Loading