Skip to content

Commit 4f26c6d

Browse files
committed
feat: add UserInviteToTenantCommand for inviting users to a tenant
1 parent 8114d69 commit 4f26c6d

3 files changed

Lines changed: 105 additions & 1 deletion

File tree

src/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ export * from "./security/permissions.list.ts"
8787

8888
// User
8989
export * from "./user/user.initialize-in-keycloak.ts"
90+
export * from "./user/user.invite-to-tenant.ts"
9091

9192
// Scenario
9293
export * from "./scenario/scenario.create.ts"
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Type } from "@sinclair/typebox"
2+
import { Command } from "../../common/command.ts"
3+
import { parseResponseHelper } from "../../utils/parse-response-helper.ts"
4+
import type { Static, TBoolean, TObject, TString } from "@sinclair/typebox"
5+
6+
/**
7+
* The input for inviting a user to a tenant
8+
*/
9+
export interface UserInviteToTenantInput {
10+
tenantName: string
11+
userEmail: string
12+
}
13+
14+
/**
15+
* The output for inviting a user to a tenant
16+
*/
17+
export type UserInviteToTenantOutput = Static<typeof responseSchema>
18+
19+
const responseSchema: TObject<{
20+
success: TBoolean
21+
tenantName: TString
22+
invitedEmail: TString
23+
}> = Type.Object({
24+
success: Type.Boolean(),
25+
tenantName: Type.String(),
26+
invitedEmail: Type.String(),
27+
})
28+
29+
/**
30+
* Invite an existing user (by email) to a tenant
31+
*/
32+
export class UserInviteToTenantCommand extends Command<
33+
UserInviteToTenantInput,
34+
UserInviteToTenantOutput
35+
> {
36+
/**
37+
* The allowed modes for the command
38+
*/
39+
protected override allowedModes: ("apiKey" | "bearer")[] = ["bearer"]
40+
41+
/**
42+
* Get the base URL for the request
43+
*/
44+
protected override getBaseUrl(): string {
45+
return "https://user-2.api.flowcore.io"
46+
}
47+
48+
/**
49+
* Get the method
50+
*/
51+
protected override getMethod(): string {
52+
return "POST"
53+
}
54+
55+
/**
56+
* Get the path for the request
57+
*/
58+
protected override getPath(): string {
59+
return "/api/users/invitations"
60+
}
61+
62+
/**
63+
* Parse the response
64+
*/
65+
protected override parseResponse(rawResponse: unknown): UserInviteToTenantOutput {
66+
const response = parseResponseHelper(responseSchema, rawResponse)
67+
return response
68+
}
69+
}

test/tests/commands/user.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import { assertEquals } from "@std/assert"
22
import { afterAll, afterEach, describe, it } from "@std/testing/bdd"
3-
import { FlowcoreClient, UserInitializeInKeycloakCommand } from "../../../src/mod.ts"
3+
import {
4+
FlowcoreClient,
5+
UserInitializeInKeycloakCommand,
6+
UserInviteToTenantCommand,
7+
} from "../../../src/mod.ts"
48
import { FetchMocker } from "../../fixtures/fetch.fixture.ts"
59

610
describe("User", () => {
@@ -68,4 +72,34 @@ describe("User", () => {
6872
assertEquals(response, userData)
6973
})
7074
})
75+
76+
describe("UserInviteToTenantCommand", () => {
77+
it("should POST /api/users/invitations and return invitation response", async () => {
78+
// arrange
79+
const requestBody = {
80+
tenantName: "example-tenant",
81+
userEmail: "user@example.com",
82+
}
83+
const responseData = {
84+
success: true,
85+
tenantName: requestBody.tenantName,
86+
invitedEmail: requestBody.userEmail,
87+
}
88+
89+
fetchMockerBuilder.post("/api/users/invitations")
90+
.matchHeaders({
91+
Authorization: "Bearer BEARER_TOKEN",
92+
"Content-Type": "application/json",
93+
})
94+
.matchBody(requestBody)
95+
.respondWith(200, responseData)
96+
97+
// act
98+
const command = new UserInviteToTenantCommand(requestBody)
99+
const response = await flowcoreClient.execute(command)
100+
101+
// assert
102+
assertEquals(response, responseData)
103+
})
104+
})
71105
})

0 commit comments

Comments
 (0)