テナント管理

mindzieStudioプラットフォームでテナントの作成、一覧取得、取得、および更新を行います。

重要: このページのすべてのエンドポイントにはグローバルAPIキーが必要です。テナント固有のAPIキーを使用すると、401 Unauthorizedエラーが返されます。


すべてのテナントを一覧表示

GET /api/tenant

システム内のすべてのテナントのページネーションされたリストと概要統計を取得します。

クエリパラメータ

パラメータ タイプ デフォルト 説明
page integer 1 ページネーションのページ番号
pageSize integer 50 1ページあたりのアイテム数(最大100)

レスポンス (200 OK)

{
  "tenants": [
    {
      "tenantId": "12345678-1234-1234-1234-123456789012",
      "name": "acme-corp",
      "displayName": "Acme Corporation",
      "description": "Main tenant for Acme Corporation",
      "caseCount": 50000,
      "maxUserCount": 100,
      "maxAnalystCount": 20,
      "analystCount": 12,
      "userCount": 45,
      "preRelease": false,
      "isAcademic": false,
      "autoload": true,
      "dateCreated": "2024-01-15T10:30:00Z",
      "isDisabled": false
    }
  ],
  "totalCount": 5,
  "page": 1,
  "pageSize": 50
}

テナントオブジェクトのフィールド

フィールド タイプ 説明
tenantId GUID テナントの一意な識別子
name string 一意のシステム名(URLで使用される)
displayName string 人間が読みやすい表示名
description string テナントの説明
caseCount integer すべてのデータセットにわたるケースの総数
maxUserCount integer 最大許可ユーザー数
maxAnalystCount integer 最大許可アナリスト数
analystCount integer 現在のアナリスト数
userCount integer 現在のユーザー数
preRelease boolean テナントがプリリリース機能を持つかどうか
isAcademic boolean 学術用テナントかどうか
autoload boolean プロジェクトの自動読み込み設定
dateCreated datetime テナントの作成日時
isDisabled boolean テナントが無効化されているかどうか

エラー応答

Unauthorized (401):

{
  "error": "This endpoint requires a Global API key. Tenant-specific API keys cannot list all tenants.",
  "hint": "Global API keys can be created at /admin/global-api-keys"
}

テナントをIDで取得

GET /api/tenant/{tenantId}

指定したIDのテナントの詳細情報を取得します。

パスパラメータ

パラメータ タイプ 必須 説明
tenantId GUID はい テナントの一意な識別子

レスポンス (200 OK)

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation",
  "description": "Main tenant for Acme Corporation",
  "isAcademic": false,
  "preRelease": false,
  "maxUserCount": 100,
  "maxAnalystCount": 20,
  "maxCases": 100000,
  "dateCreated": "2024-01-15T10:30:00Z",
  "isDisabled": false
}

エラー応答

Not Found (404):

{
  "error": "Tenant with ID '12345678-1234-1234-1234-123456789012' not found"
}

テナントを作成

POST /api/tenant

必要なインフラストラクチャすべてを含む、新しいテナントをシステムに作成します。

リクエストボディ

{
  "name": "new-tenant",
  "displayName": "New Tenant Corp",
  "description": "Description of the new tenant",
  "maxUsers": 50,
  "maxAnalyst": 10,
  "maxCases": 100000,
  "timeZone": "America/New_York"
}

リクエストフィールド

フィールド タイプ 必須 説明
name string はい 一意のシステム名(3~63文字、小文字の英数字とハイフンのみ)
displayName string はい 人間が読みやすい表示名
description string いいえ テナントの説明
maxUsers integer はい 最大ユーザー数
maxAnalyst integer はい 最大アナリスト数
maxCases integer はい 最大ケース数
timeZone string いいえ テナントのタイムゾーン

テナント名の要件

  • 3~63文字
  • 小文字の英数字およびハイフンのみ
  • スペースや特殊文字は不可
  • 全テナントで一意であること

レスポンス (201 Created)

{
  "tenantId": "aabbccdd-1234-1234-1234-123456789012",
  "name": "new-tenant",
  "displayName": "New Tenant Corp",
  "message": "Tenant 'New Tenant Corp' created successfully",
  "storageContainerCreated": true
}

エラー応答

Conflict (409):

{
  "error": "A tenant with name 'new-tenant' already exists"
}

License Limit (429):

{
  "error": "Maximum number of tenants reached. Your license allows 10 tenants.",
  "hint": "Upgrade your license to create more tenants"
}

Validation Error (400):

{
  "error": "Validation failed",
  "validationErrors": [
    "Name must be between 3 and 63 characters",
    "Name can only contain lowercase letters, numbers, and hyphens"
  ]
}

テナントを更新

PUT /api/tenant

既存のテナントのプロパティを更新します。指定されたフィールドのみが更新され、null値は無視されます。

リクエストボディ

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "displayName": "Acme Corporation Updated",
  "description": "Updated description",
  "maxUsers": 100,
  "maxAnalyst": 25,
  "maxCases": 200000,
  "timeZone": "America/New_York",
  "isAcademic": false,
  "preRelease": true,
  "isDisabled": false
}

リクエストフィールド

フィールド タイプ 必須 説明
tenantId GUID はい 更新対象のテナント
displayName string いいえ 新しい表示名(nullなら変更なし)
description string いいえ 新しい説明(null=変更なし、""=クリア)
maxUsers integer いいえ 最大ユーザー数(nullなら変更なし)
maxAnalyst integer いいえ 最大アナリスト数(nullなら変更なし)
maxCases integer いいえ 最大ケース数(null=変更なし、-1=無制限)
timeZone string いいえ タイムゾーンID(nullなら変更なし)
isAcademic boolean いいえ 学術用フラグ(nullなら変更なし)
preRelease boolean いいえ プリリリース機能(nullなら変更なし)
isDisabled boolean いいえ テナント無効化(nullなら変更なし)

注意: 作成後はテナントの name を変更できません。

レスポンス (200 OK)

{
  "tenantId": "12345678-1234-1234-1234-123456789012",
  "name": "acme-corp",
  "displayName": "Acme Corporation Updated",
  "message": "Tenant 'acme-corp' updated successfully",
  "isDisabled": false
}

エラー応答

Not Found (404):

{
  "error": "Tenant with ID '12345678-1234-1234-1234-123456789012' not found"
}

Validation Error (400):

{
  "error": "Validation failed",
  "validationErrors": ["Display name cannot exceed 255 characters"]
}

実装例

cURL

# すべてのテナント一覧取得
curl -X GET "https://your-mindzie-instance.com/api/tenant?page=1&pageSize=50" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY"

# 特定のテナントをIDで取得
curl -X GET "https://your-mindzie-instance.com/api/tenant/12345678-1234-1234-1234-123456789012" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY"

# テナント作成
curl -X POST "https://your-mindzie-instance.com/api/tenant" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "new-tenant",
    "displayName": "New Tenant Corp",
    "description": "Test tenant",
    "maxUsers": 50,
    "maxAnalyst": 10,
    "maxCases": 100000
  }'

# テナント更新
curl -X PUT "https://your-mindzie-instance.com/api/tenant" \
  -H "Authorization: Bearer YOUR_GLOBAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "12345678-1234-1234-1234-123456789012",
    "displayName": "New Tenant Corp Updated",
    "maxUsers": 100,
    "isDisabled": false
  }'

Python

import requests

BASE_URL = 'https://your-mindzie-instance.com'

class TenantManager:
    def __init__(self, global_api_key):
        """グローバルAPIキー(テナント固有ではない)で初期化します。"""
        self.headers = {
            'Authorization': f'Bearer {global_api_key}',
            'Content-Type': 'application/json'
        }

    def list_tenants(self, page=1, page_size=50):
        """システム内のすべてのテナントを一覧表示します。"""
        url = f'{BASE_URL}/api/tenant'
        params = {'page': page, 'pageSize': page_size}
        response = requests.get(url, headers=self.headers, params=params)
        response.raise_for_status()
        return response.json()

    def get_tenant(self, tenant_id):
        """指定したIDのテナントを取得します。"""
        url = f'{BASE_URL}/api/tenant/{tenant_id}'
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def create_tenant(self, name, display_name, description='', max_users=50,
                      max_analyst=10, max_cases=100000, timezone=None):
        """新しいテナントを作成します。"""
        url = f'{BASE_URL}/api/tenant'
        payload = {
            'name': name,
            'displayName': display_name,
            'description': description,
            'maxUsers': max_users,
            'maxAnalyst': max_analyst,
            'maxCases': max_cases
        }
        if timezone:
            payload['timeZone'] = timezone

        response = requests.post(url, json=payload, headers=self.headers)
        response.raise_for_status()
        return response.json()

    def update_tenant(self, tenant_id, display_name=None, description=None,
                      max_users=None, max_analyst=None, is_disabled=None):
        """既存のテナントを更新します。"""
        url = f'{BASE_URL}/api/tenant'
        payload = {'tenantId': tenant_id}
        if display_name is not None:
            payload['displayName'] = display_name
        if description is not None:
            payload['description'] = description
        if max_users is not None:
            payload['maxUsers'] = max_users
        if max_analyst is not None:
            payload['maxAnalyst'] = max_analyst
        if is_disabled is not None:
            payload['isDisabled'] = is_disabled

        response = requests.put(url, json=payload, headers=self.headers)
        response.raise_for_status()
        return response.json()

# 使用例
manager = TenantManager('your-global-api-key')

# すべてのテナント一覧取得
result = manager.list_tenants()
print(f"テナント総数: {result['totalCount']}")
for tenant in result['tenants']:
    print(f"- {tenant['displayName']} ({tenant['name']})")
    print(f"  ユーザー数: {tenant['userCount']}/{tenant['maxUserCount']}")

# 新しいテナント作成
new_tenant = manager.create_tenant(
    name='test-tenant',
    display_name='Test Tenant',
    description='Created via API',
    max_users=25,
    max_analyst=5,
    max_cases=50000
)
print(f"作成したテナントID: {new_tenant['tenantId']}")

# テナントの制限を更新
manager.update_tenant(
    tenant_id=new_tenant['tenantId'],
    max_users=50,
    max_analyst=10
)
print("テナントの制限を更新しました")

JavaScript

const BASE_URL = 'https://your-mindzie-instance.com';

class TenantManager {
  constructor(globalApiKey) {
    this.headers = {
      'Authorization': `Bearer ${globalApiKey}`,
      'Content-Type': 'application/json'
    };
  }

  async listTenants(page = 1, pageSize = 50) {
    const url = `${BASE_URL}/api/tenant?page=${page}&pageSize=${pageSize}`;
    const response = await fetch(url, { headers: this.headers });
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    return await response.json();
  }

  async getTenant(tenantId) {
    const url = `${BASE_URL}/api/tenant/${tenantId}`;
    const response = await fetch(url, { headers: this.headers });
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    return await response.json();
  }

  async createTenant(config) {
    const url = `${BASE_URL}/api/tenant`;
    const response = await fetch(url, {
      method: 'POST',
      headers: this.headers,
      body: JSON.stringify(config)
    });
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error || `Failed: ${response.status}`);
    }
    return await response.json();
  }

  async updateTenant(tenantId, updates) {
    const url = `${BASE_URL}/api/tenant`;
    const response = await fetch(url, {
      method: 'PUT',
      headers: this.headers,
      body: JSON.stringify({ tenantId, ...updates })
    });
    if (!response.ok) throw new Error(`Failed: ${response.status}`);
    return await response.json();
  }
}

// 使用例
const manager = new TenantManager('your-global-api-key');

// テナント一覧取得
const tenants = await manager.listTenants();
console.log(`見つかったテナント数: ${tenants.totalCount}`);

// テナント作成
const newTenant = await manager.createTenant({
  name: 'new-tenant',
  displayName: 'New Tenant',
  maxUsers: 50,
  maxAnalyst: 10,
  maxCases: 100000
});
console.log(`作成されたテナントID: ${newTenant.tenantId}`);

// テナント更新
await manager.updateTenant(newTenant.tenantId, {
  displayName: 'Updated Tenant Name',
  maxUsers: 100
});

ベストプラクティス

  1. グローバルAPIキー:テナント管理にはグローバルAPIキーのみ使用してください。システム全体に対する権限があるためです。
  2. ライセンスの把握:新しいテナントを作成する前に、ライセンス制限に対してテナント数を監視してください。
  3. キャパシティプランニング:予想される利用に基づいて適切なユーザー数およびアナリスト数の制限を設定してください。
  4. 命名規則:テナント名には一貫した小文字のハイフン区切り命名規則を使用してください。