Skip to content

Commit 4220e32

Browse files
committed
feat: add UserInviteToTenantCommand to invite existing users to a tenant
1 parent aa9ef42 commit 4220e32

3 files changed

Lines changed: 184 additions & 1 deletion

File tree

src/commands/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ export * from "./security/permissions.list.ts"
8888
// User
8989
export * from "./user/user.initialize-in-keycloak.ts"
9090
export * from "./user/user.delete.ts"
91+
export * from "./user/user.invite-to-tenant.ts"
9192

9293
// Scenario
9394
export * from "./scenario/scenario.create.ts"
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
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 an existing user to a tenant
8+
*/
9+
export interface UserInviteToTenantInput {
10+
tenantName: string
11+
userEmail: string
12+
}
13+
14+
/**
15+
* The output for inviting an existing 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<UserInviteToTenantInput, UserInviteToTenantOutput> {
33+
/**
34+
* The allowed modes for the command
35+
*/
36+
protected override allowedModes: ("apiKey" | "bearer")[] = ["bearer"]
37+
38+
/**
39+
* Get the base URL for the request
40+
*/
41+
protected override getBaseUrl(): string {
42+
return "https://user-2.api.flowcore.io"
43+
}
44+
45+
/**
46+
* Get the method
47+
*/
48+
protected override getMethod(): string {
49+
return "POST"
50+
}
51+
52+
/**
53+
* Get the path for the request
54+
*/
55+
protected override getPath(): string {
56+
return "/api/users/invitations"
57+
}
58+
59+
/**
60+
* Get the body for the request
61+
*/
62+
protected override getBody(): Record<string, unknown> {
63+
return {
64+
tenantName: this.input.tenantName,
65+
userEmail: this.input.userEmail,
66+
}
67+
}
68+
69+
/**
70+
* Parse the response
71+
*/
72+
protected override parseResponse(rawResponse: unknown): UserInviteToTenantOutput {
73+
const response = parseResponseHelper(responseSchema, rawResponse)
74+
return response
75+
}
76+
}
77+
import { Type } from "@sinclair/typebox"
78+
import { Command } from "../../common/command.ts"
79+
import { parseResponseHelper } from "../../utils/parse-response-helper.ts"
80+
import type { Static, TBoolean, TObject, TString } from "@sinclair/typebox"
81+
82+
/**
83+
* The input for inviting a user to a tenant
84+
*/
85+
export interface UserInviteToTenantInput {
86+
tenantName: string
87+
userEmail: string
88+
}
89+
90+
/**
91+
* The output for inviting a user to a tenant
92+
*/
93+
export type UserInviteToTenantOutput = Static<typeof responseSchema>
94+
95+
const responseSchema: TObject<{
96+
success: TBoolean
97+
tenantName: TString
98+
invitedEmail: TString
99+
}> = Type.Object({
100+
success: Type.Boolean(),
101+
tenantName: Type.String(),
102+
invitedEmail: Type.String(),
103+
})
104+
105+
/**
106+
* Invite an existing user (by email) to a tenant
107+
*/
108+
export class UserInviteToTenantCommand extends Command<
109+
UserInviteToTenantInput,
110+
UserInviteToTenantOutput
111+
> {
112+
/**
113+
* The allowed modes for the command
114+
*/
115+
protected override allowedModes: ("apiKey" | "bearer")[] = ["bearer"]
116+
117+
/**
118+
* Get the base URL for the request
119+
*/
120+
protected override getBaseUrl(): string {
121+
return "https://user-2.api.flowcore.io"
122+
}
123+
124+
/**
125+
* Get the method
126+
*/
127+
protected override getMethod(): string {
128+
return "POST"
129+
}
130+
131+
/**
132+
* Get the path for the request
133+
*/
134+
protected override getPath(): string {
135+
return "/api/users/invitations"
136+
}
137+
138+
/**
139+
* Parse the response
140+
*/
141+
protected override parseResponse(rawResponse: unknown): UserInviteToTenantOutput {
142+
const response = parseResponseHelper(responseSchema, rawResponse)
143+
return response
144+
}
145+
}

test/tests/commands/user.test.ts

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

611
describe("User", () => {
@@ -90,4 +95,36 @@ describe("User", () => {
9095
assertEquals(response, responseData)
9196
})
9297
})
98+
99+
describe("UserInviteToTenantCommand", () => {
100+
it("should POST /api/users/invitations and return invitation response", async () => {
101+
// arrange
102+
const responseData = {
103+
success: true,
104+
tenantName: "acme",
105+
invitedEmail: "invitee@example.com",
106+
}
107+
108+
fetchMockerBuilder.post("/api/users/invitations")
109+
.matchHeaders({
110+
Authorization: "Bearer BEARER_TOKEN",
111+
"Content-Type": "application/json",
112+
})
113+
.matchBody({
114+
tenantName: "acme",
115+
userEmail: "invitee@example.com",
116+
})
117+
.respondWith(200, responseData)
118+
119+
// act
120+
const command = new UserInviteToTenantCommand({
121+
tenantName: "acme",
122+
userEmail: "invitee@example.com",
123+
})
124+
const response = await flowcoreClient.execute(command)
125+
126+
// assert
127+
assertEquals(response, responseData)
128+
})
129+
})
93130
})

0 commit comments

Comments
 (0)