# Organizations Module

# Organizations Module

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [infra/init-db/01-create-casdoor-db.sql](infra/init-db/01-create-casdoor-db.sql)
- [src/modules/auth/config-repo.ts](src/modules/auth/config-repo.ts)
- [src/modules/auth/config.ts](src/modules/auth/config.ts)
- [src/modules/auth/organization_entity.ts](src/modules/auth/organization_entity.ts)
- [src/modules/auth/organizations_dal.ts](src/modules/auth/organizations_dal.ts)
- [src/modules/auth/organizations_router.ts](src/modules/auth/organizations_router.ts)

</details>



The **Organizations Module** manages the lifecycle of organizational entities within Primebrick, serving as a synchronized mirror of Casdoor organizations. It implements a **Casdoor-first write strategy**, ensuring that any changes to organizations are first committed to the Identity Provider (IDP) before being persisted in the local PostgreSQL database.

## Architectural Overview

The module is built around the `OrganizationEntity`, which maps local UUIDs to Casdoor's `idp_code` (owner/name format). This decoupling allows the application to use immutable internal identifiers while maintaining compatibility with the external IDP.

### Data Flow: Dual-Write Synchronization
When an organization is created or updated, the system performs a synchronous call to the Casdoor API. If the IDP operation fails, the local transaction is not committed (or the local write is not initiated), ensuring data consistency between Primebrick and Casdoor.

### Organization Identity Mapping
| Field | Type | Role |
| :--- | :--- | :--- |
| `uuid` | `string` | Internal Primebrick identifier used for all foreign keys and API references [src/modules/auth/organization_entity.ts:39-39](). |
| `idp_code` | `string` | Casdoor identifier (e.g., `admin/acme`). Immutable after creation [src/modules/auth/organization_entity.ts:41-44](). |
| `idp_owner` | `string` | The owner prefix in Casdoor (usually the IDP organization name) [src/modules/auth/organization_entity.ts:45-46](). |
| `idp_name` | `string` | The specific organization name within the IDP [src/modules/auth/organization_entity.ts:48-49](). |

**Sources:** [src/modules/auth/organization_entity.ts:1-15](), [src/modules/auth/organizations_dal.ts:16-32]()

---

## Code Entity Map: Router to DAL
The following diagram illustrates the relationship between the HTTP layer, the Data Access Layer (DAL), and the underlying Entity definitions.

**Diagram: Organizations Module Component Mapping**
```mermaid
graph TD
    subgraph "HTTP Layer (organizations_router.ts)"
        R["organizationsRouter()"]
        M["GET /meta"]
        L["GET /list"]
        C["POST /create"]
        U["PUT /update/:uuid"]
    end

    subgraph "Data Access Layer (organizations_dal.ts)"
        DAL["OrganizationsDal"]
        DAL_L["listOrganizations()"]
        DAL_C["createOrganization()"]
        DAL_U["updateOrganization()"]
        DAL_GC["getUserCountForOrganization()"]
    end

    subgraph "Domain & Persistence"
        E["OrganizationEntity"]
        REPO["Repository (Generic)"]
        CAS["CasdoorApiClient"]
    end

    R --> DAL
    L --> DAL_L
    C --> DAL_C
    U --> DAL_U
    
    DAL_L --> REPO
    DAL_C --> CAS
    DAL_C --> REPO
    DAL_U --> CAS
    DAL_U --> REPO
    
    REPO -.-> E
```
**Sources:** [src/modules/auth/organizations_router.ts:13-20](), [src/modules/auth/organizations_dal.ts:63-70]()

---

## Data Access Layer (DAL)

The `OrganizationsDal` handles the complexity of joining audit trails and calculating aggregate data like user counts.

### Key Methods
*   **`listOrganizations(query)`**: Implements a complex pipeline using the Repository DSL. It filters by deletion status (`EXCLUDED`, `ONLY`, `INCLUDED`), performs `ILIKE` searches on `display_name` and `idp_code`, and executes a `LEFT JOIN` on `UserProfileEntity` to resolve audit user display names [src/modules/auth/organizations_dal.ts:106-180]().
*   **`getUserCountForOrganization(idpCode)`**: Executes a direct count query against the `user_profiles` table to determine how many users belong to a specific organization [src/modules/auth/organizations_dal.ts:254-263]().
*   **`createOrganization(data)`**: Generates a new `uuid`, assigns the current session actor to `created_by`, and persists the entity [src/modules/auth/organizations_dal.ts:198-220]().

### Soft-Delete and Restore
The DAL implements soft-deletion by populating the `deleted_at` and `deleted_by` fields instead of removing the row.
*   **Delete**: `repo.update` sets the deletion timestamp and actor [src/modules/auth/organizations_dal.ts:284-295]().
*   **Restore**: Sets `deleted_at` and `deleted_by` to `null` [src/modules/auth/organizations_dal.ts:303-314]().

**Sources:** [src/modules/auth/organizations_dal.ts:63-315]()

---

## Casdoor Integration

The module uses `CasdoorApiClient` to synchronize changes. The client is initialized JIT (Just-In-Time) using credentials stored in the `auth_configurations` table [src/modules/auth/organizations_router.ts:24-44]().

**Write Synchronization Logic:**
1.  **Validation**: Request body is validated via Zod schemas in the router.
2.  **IDP Write**: The router calls `getCasdoorClient()` and invokes the IDP update/create.
3.  **Local Write**: If the IDP call succeeds, `OrganizationsDal` is called to update the PostgreSQL mirror.
4.  **Audit**: The `Repository` automatically calculates the delta and writes to the `organizations_audit` table.

**Diagram: Create Organization Sequence**
```mermaid
sequenceDiagram
    participant Client
    participant Router as organizations_router
    participant IDP as CasdoorApiClient
    participant DAL as OrganizationsDal
    participant DB as PostgreSQL

    Client->>Router: POST /api/v1/entities/organization/create
    Router->>Router: validateBody(createSchema)
    Router->>IDP: addOrganization(data)
    Note over IDP: External API Call
    IDP-->>Router: Success
    Router->>DAL: createOrganization(data)
    DAL->>DB: INSERT INTO organizations
    DAL->>DB: INSERT INTO organizations_audit (delta)
    DB-->>DAL: OK
    DAL-->>Router: { uuid }
    Router-->>Client: 201 Created
```
**Sources:** [src/modules/auth/organizations_router.ts:24-44](), [src/modules/auth/organizations_dal.ts:198-220](), [src/modules/auth/organization_entity.ts:32-34]()

---

## Entity Configuration

The `OrganizationEntity` uses decorators to define its behavior within the Primebrick Domain System.

| Decorator | Purpose |
| :--- | :--- |
| `@Entity("organizations")` | Maps the class to the `organizations` table [src/modules/auth/organization_entity.ts:32-32](). |
| `@AuditTrail()` | Enables automatic tracking of all changes in a shadow audit table [src/modules/auth/organization_entity.ts:33-33](). |
| `@SynchronizableField` | Marks `last_synced_at` for tracking IDP synchronization state [src/modules/auth/organization_entity.ts:57-59](). |
| `@AuditableField` | Defines standard fields like `created_at`, `updated_by`, and `version` for optimistic concurrency [src/modules/auth/organization_entity.ts:62-75](). |
| `@DeletableField` | Hooks into the soft-delete system [src/modules/auth/organization_entity.ts:77-81](). |

**Sources:** [src/modules/auth/organization_entity.ts:17-82]()

---
