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
1 change: 1 addition & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export * from "./security/permissions.list.ts"
// User
export * from "./user/user.initialize-in-keycloak.ts"
export * from "./user/user.delete.ts"
export * from "./user/user.invite-to-tenant.ts"

// Scenario
export * from "./scenario/scenario.create.ts"
Expand Down
79 changes: 79 additions & 0 deletions src/commands/user/user.invite-to-tenant.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Type } from "@sinclair/typebox"
import { Command } from "../../common/command.ts"
import { parseResponseHelper } from "../../utils/parse-response-helper.ts"
import type { Static, TBoolean, TObject, TString } from "@sinclair/typebox"

/**
* The input for inviting an existing user to a tenant
*/
export interface UserInviteToTenantInput {
tenantName: string
userEmail: string
}

/**
* The output for inviting an existing user to a tenant
*/
export type UserInviteToTenantOutput = Static<typeof responseSchema>

const responseSchema: TObject<{
success: TBoolean
tenantName: TString
invitedEmail: TString
}> = Type.Object({
success: Type.Boolean(),
tenantName: Type.String(),
invitedEmail: Type.String(),
})

/**
* Invite an existing user (by email) to a tenant
*/
export class UserInviteToTenantCommand extends Command<
UserInviteToTenantInput,
UserInviteToTenantOutput
> {
/**
* The allowed modes for the command
*/
protected override allowedModes: ("apiKey" | "bearer")[] = ["bearer"]

/**
* Get the base URL for the request
*/
protected override getBaseUrl(): string {
return "https://user-2.api.flowcore.io"
}

/**
* Get the method
*/
protected override getMethod(): string {
return "POST"
}

/**
* Get the path for the request
*/
protected override getPath(): string {
return "/api/users/invitations"
}

/**
* Get the body for the request
*/
protected override getBody(): Record<string, unknown> {
return {
tenantName: this.input.tenantName,
userEmail: this.input.userEmail,
}
}

/**
* Parse the response
*/
protected override parseResponse(rawResponse: unknown): UserInviteToTenantOutput {
const response = parseResponseHelper(responseSchema, rawResponse)
return response
}
}
39 changes: 38 additions & 1 deletion test/tests/commands/user.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { assertEquals } from "@std/assert"
import { afterAll, afterEach, describe, it } from "@std/testing/bdd"
import { FlowcoreClient, UserDeleteCommand, UserInitializeInKeycloakCommand } from "../../../src/mod.ts"
import {
FlowcoreClient,
UserDeleteCommand,
UserInitializeInKeycloakCommand,
UserInviteToTenantCommand,
} from "../../../src/mod.ts"
import { FetchMocker } from "../../fixtures/fetch.fixture.ts"

describe("User", () => {
Expand Down Expand Up @@ -90,4 +95,36 @@ describe("User", () => {
assertEquals(response, responseData)
})
})

describe("UserInviteToTenantCommand", () => {
it("should POST /api/users/invitations and return invitation response", async () => {
// arrange
const responseData = {
success: true,
tenantName: "acme",
invitedEmail: "invitee@example.com",
}

fetchMockerBuilder.post("/api/users/invitations")
.matchHeaders({
Authorization: "Bearer BEARER_TOKEN",
"Content-Type": "application/json",
})
.matchBody({
tenantName: "acme",
userEmail: "invitee@example.com",
})
.respondWith(200, responseData)

// act
const command = new UserInviteToTenantCommand({
tenantName: "acme",
userEmail: "invitee@example.com",
})
const response = await flowcoreClient.execute(command)

// assert
assertEquals(response, responseData)
})
})
})