# ManageEngine MDM API Client

[![npm package][npm-img]][npm-url]
[![Build Status][build-img]][build-url]
[![Downloads][downloads-img]][downloads-url]
[![Issues][issues-img]][issues-url]
[![Code Coverage][codecov-img]][codecov-url]
[![Commitizen Friendly][commitizen-img]][commitizen-url]
[![Semantic Release][semantic-release-img]][semantic-release-url]

A comprehensive TypeScript client for the ManageEngine Mobile Device Manager Plus API. This client provides type-safe access to MDM functionality including device management, app distribution, compliance policies, and more.

## Table of Contents

- [Installation](#installation)
- [Quick Start](#quick-start)
- [Authentication](#authentication)
  - [Token Storage](#token-storage)
  - [Available Scopes](#available-scopes)
- [API Reference](#api-reference)
  - [Device Management](#device-management)
  - [Group Management](#group-management)
  - [Profile Management](#profile-management)
  - [App Management](#app-management)
  - [Compliance & Geofencing](#compliance--geofencing)
  - [Scheduled Actions](#scheduled-actions)
  - [VPP Management](#vpp-management)
  - [Content Management](#content-management)
  - [User Management](#user-management)
  - [Enrollment Settings](#enrollment-settings)
  - [Announcements](#announcements)
  - [Blocklist Management](#blocklist-management)
- [Error Handling](#error-handling)
- [TypeScript Support](#typescript-support)
- [Contributing](#contributing)
- [License](#license)

## Installation

```bash
npm install manageengine-mdm
```

## Quick Start

```typescript
import { ManageEngineMDM, Scope } from 'manageengine-mdm';

// Initialize the client
const mdm = new ManageEngineMDM({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  baseUrl: 'https://mdm.manageengine.com',
  redirectUri: 'https://your-app.com/oauth/callback',
  scope: [Scope.DEVICE_MGMT_READ, Scope.DEVICE_MGMT_UPDATE],
});

// Get authorization URL
const authUrl = mdm.getAuthorizationUrl('offline', 'consent');

// Initialize with authorization code
await mdm.initialize(code, accountsServer);

// Or initialize with refresh token
await mdm.initializeWithRefreshToken(refreshToken, accountsServer);

// Check authentication status
const isAuthenticated = await mdm.isAuthenticated();

// Get current auth state
const authState = await mdm.getAuthState();

// Clear auth state when needed
await mdm.clearAuthState();
```

## Authentication

The client uses OAuth 2.0 for authentication. You can authenticate using either an authorization code flow or directly with a refresh token.

### Token Storage

By default, tokens are stored in memory, but you can provide a custom token storage implementation to persist tokens in Redis, a database, or any other storage system.

```typescript
import { TokenStorage, AuthState } from 'manageengine-mdm';

// Example Redis implementation
class RedisTokenStorage implements TokenStorage {
  private readonly redis: Redis;

  constructor(redis: Redis) {
    this.redis = redis;
  }

  async saveToken(accountsServer: string, state: AuthState): Promise<void> {
    await this.redis.set(`mdm:token:${accountsServer}`, JSON.stringify(state));
  }

  async getToken(accountsServer: string): Promise<AuthState | undefined> {
    const token = await this.redis.get(`mdm:token:${accountsServer}`);
    return token ? JSON.parse(token) : undefined;
  }

  async clearToken(accountsServer: string): Promise<void> {
    await this.redis.del(`mdm:token:${accountsServer}`);
  }
}

// Example database implementation
class DatabaseTokenStorage implements TokenStorage {
  async saveToken(accountsServer: string, state: AuthState): Promise<void> {
    await db.tokens.upsert({
      accountsServer,
      state: JSON.stringify(state),
    });
  }

  async getToken(accountsServer: string): Promise<AuthState | undefined> {
    const record = await db.tokens.findOne({ accountsServer });
    return record ? JSON.parse(record.state) : undefined;
  }

  async clearToken(accountsServer: string): Promise<void> {
    await db.tokens.delete({ accountsServer });
  }
}

// Use custom storage when initializing the client
const mdm = new ManageEngineMDM({
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  baseUrl: 'https://mdm.manageengine.com',
  redirectUri: 'https://your-app.com/oauth/callback',
  scope: [Scope.DEVICE_MGMT_READ],
  tokenStorage: new RedisTokenStorage(redisClient), // Your custom storage
});
```

### Available Scopes

```typescript
enum Scope {
  // Inventory Management
  INVENTORY_CREATE = 'MDMOnDemand.MDMInventory.CREATE',
  INVENTORY_UPDATE = 'MDMOnDemand.MDMInventory.UPDATE',
  INVENTORY_READ = 'MDMOnDemand.MDMInventory.READ',
  INVENTORY_DELETE = 'MDMOnDemand.MDMInventory.DELETE',
  INVENTORY_ALL = 'MDMOnDemand.MDMInventory.ALL',

  // Device Management
  DEVICE_MGMT_CREATE = 'MDMOnDemand.MDMDeviceMgmt.CREATE',
  DEVICE_MGMT_UPDATE = 'MDMOnDemand.MDMDeviceMgmt.UPDATE',
  DEVICE_MGMT_READ = 'MDMOnDemand.MDMDeviceMgmt.READ',
  DEVICE_MGMT_DELETE = 'MDMOnDemand.MDMDeviceMgmt.DELETE',
  DEVICE_MGMT_ALL = 'MDMOnDemand.MDMDeviceMgmt.ALL',

  // User Management
  USER_CREATE = 'MDMOnDemand.MDMUser.CREATE',
  USER_UPDATE = 'MDMOnDemand.MDMUser.UPDATE',
  USER_READ = 'MDMOnDemand.MDMUser.READ',
  USER_DELETE = 'MDMOnDemand.MDMUser.DELETE',
  USER_ALL = 'MDMOnDemand.MDMUser.ALL',
}
```

## API Reference

### Device Management

#### List Devices

```typescript
const devices = await mdm.devices.listDevices({
  search?: string;
  platform?: ('iOS' | 'Android' | 'Windows')[];
  exclude_removed?: boolean;
  imei?: number;
  owned_by?: 'Corporate' | 'Personal';
  device_type?: ('smartphone' | 'Tablet' | 'Laptop' | 'Desktop' | 'Tv')[];
  serial_number?: string;
  email?: string;
});
```

#### Get Device Details

```typescript
const device = await mdm.devices.getDevice(deviceId, summary);
```

#### Device Actions

```typescript
// Lock device
await mdm.devices.performAction(deviceId, 'lock', {
  lock_message: 'Device locked for security',
});

// Wipe device
await mdm.devices.performAction(deviceId, 'complete_wipe', {
  wipe_sd_card: true,
});
```

### Compliance & Geofencing

The Compliance API allows you to create and manage geofencing profiles and compliance policies.

#### List Fence Profiles

```typescript
const profiles = await mdm.compliance.listFenceProfiles();

// Response type:
interface CompliancePoliciesResponse {
  compliance_policies: Array<{
    compliance_id: number;
    compliance_name: string;
    platform_type: number;
    created_by: number;
    creation_time: number;
    last_modified_time: number;
    compliant_devices_count: number;
    non_compliant_devices_count: number;
  }>;
}
```

#### Create Fence Profile

```typescript
const profile = await mdm.compliance.addFenceProfile({
  policies: [{
    rule: {
      rule_name: string;
      rule_criterions: Array<{
        rule_criteria_type: number;
        geo_fence_id: number;
        rule_criteria_state: number;
      }>;
      evaluation_order: number;
    },
    action: {
      action_attributes: Array<{
        execution_order: number;
        action_attribute_type: number;
        lock_message?: string;
        unlock_pin?: string;
        is_remote_alarm: boolean;
        is_mark_as_non_compliant: boolean;
        alert_email_ids?: Array<{
          email: string;
          user_name: string;
        }>;
      }>;
    }
  }],
  description: string;
  compliance_name: string;
});

// Response type:
interface ComplianceCreateResponse {
  collection_id: number;
  is_moved_to_trash: boolean;
  compliance_id: number;
}
```

#### Modify Fence Profile

```typescript
const updated = await mdm.compliance.modifyFenceProfile(
  profileId,
  {
    policies: Array<Policy>;
    description: string;
    compliance_name: string;
  }
);

// Response type:
interface ComplianceUpdateResponse {
  collection_id: number;
  user_id: number;
  compliance_file_name_path: string;
  remove_compliance_file_name_path: string;
  customer_id: number;
  compliance_id: number;
  compliance_name: string;
}
```

#### Group Management

```typescript
// Get associated groups
const groups = await mdm.compliance.getFenceGroups(complianceId);

// Response type:
interface ComplianceGroupListResponse {
  group_list: Array<{
    group_id: number;
    group_name: string;
    group_type: number;
    member_count: number;
  }>;
  compliance_id: number;
}

// Associate with groups
await mdm.compliance.associateMultipleGroups(complianceId, {
  group_ids: string[];
});

// Get group devices
const devices = await mdm.compliance.getGroupDevices(complianceId, groupId);

// Response type:
interface ComplianceGroupDetailsResponse {
  group_id: number;
  group_name: string;
  devices: Array<{
    device_id: number;
    device_name: string;
    device_platform: number;
    device_compliance_state: string;
    compliance_score: number;
  }>;
  group_compliance_state: string;
  compliance_id: number;
  compliance_name: string;
}
```

### Scheduled Actions

The Scheduled Actions API enables scheduling of device operations like restart and shutdown.

#### Create Schedule

```typescript
const schedule = await mdm.scheduledActions.createSchedule({
  groups: string[];           // Group IDs
  time_zone: string;         // Timezone for scheduling
  schedule_params: {
    schedule_type: string;   // Schedule type
    monthly_perform?: string;
    daily_time?: string;
    monthly_week_day?: number;
    days_of_week?: string;
    daily_interval_type?: string;
    scheduler_disabled?: boolean;
    monthly_time?: number;
    monthly_week_num?: number;
    weekly_time?: number;
    monthly_day?: number;
    months_list?: string;
  };
  reason_message: string;
  execution_type: number;    // 1 = Once, 2 = Repeat
  expiry: number;           // Expiry timestamp
  schedule_once_time: number; // Execution timestamp
  action_name: 'restart' | 'shutdown';
});

// Response type:
interface CreateScheduleResponse {
  group_action_id: number;
  status: string;
}
```

#### Modify Schedule

```typescript
const result = await mdm.scheduledActions.modifySchedule({
  group_action_id: number;   // ID of schedule to modify
  // ... same parameters as createSchedule
});

// Response type:
interface ModifyScheduleResponse {
  status: string;
}
```

#### Delete Schedule

```typescript
const result = await mdm.scheduledActions.deleteSchedule({
  group_action_id: number;
  action_name: 'restart' | 'shutdown';
});

// Response type:
interface DeleteScheduleResponse {
  status: string;
}
```

#### Validate Schedule

```typescript
const validation = await mdm.scheduledActions.validateSchedule({
  groups: string[];
  action_name: 'restart' | 'shutdown';
});

// Response type:
interface ValidateScheduleResponse {
  valid: boolean;
  invalid_groups?: string[];
  reason?: string;
}
```

### VPP Management

The VPP API manages Volume Purchase Program accounts and tokens for iOS app distribution.

#### List VPP Accounts

```typescript
const accounts = await mdm.vpp.getAllVppAccounts();

// Response type:
interface VppAccountResponse {
  non_vpp_app_count: number;
  vpp_token_details: Array<{
    businessstore_id: number;
    organisation_name: string;
    license_assign_type: number;
    location_name: string;
  }>;
  trash_count: number;
}
```

#### Add VPP Account

```typescript
const account = await mdm.vpp.addVppAccount({
  vpp_token_file: number;    // File ID of server token
  email_address: string;     // Notification email
});

// Response type:
interface AddVppAccountResponse {
  location_name: string;
  expired: boolean;
  businessstore_id: number;
  organisation_name: string;
  expiry_date: number;
}
```

#### VPP Account Operations

```typescript
// Get account details
const details = await mdm.vpp.getVppAccount(vppId);

// Response type:
interface VppAccountDetail {
  non_vpp_apps_count: number;
  location_name: string;
  total_apps_count: number;
  expiry_date: number;
  last_sync_time: number;
  org_type: number;
  organization_name: string;
  businessstore_id: number;
  license_assign_type: number;
}

// Modify account
await mdm.vpp.modifyVppAccount(vppId, {
  vpp_token_file: number;
  email_address: string;
});

// Remove account
await mdm.vpp.removeVppAccount(vppId);
```

#### VPP Sync Operations

```typescript
// Get sync status
const status = await mdm.vpp.getVppSyncStatus(vppId);

// Response type:
interface VppSyncStatusResponse {
  if_license_insufficient: boolean;
  if_sync_failed: boolean;
  apps_with_insufficient_licenses: number;
  failed_apps_count: number;
  total_apps_count: number;
  successful_apps_count: number;
  last_sync_time: number;
  completed_apps_count: number;
  remarks: string | null;
  status: number;
  other_mdm_hostname: string;
}

// Sync account
await mdm.vpp.syncVppAccount(vppId, {
  remove_from_other_mdm: boolean;
});

// Get failure details
const failures = await mdm.vpp.getVppFailureDetails(vppId);

// Response type:
interface VppFailureResponse {
  apps: Array<{
    appgroupid: number;
    appname: string;
    displayimageloc: string;
    licensecount: number;
    packageid: number;
    resourcecount: number;
  }>;
}
```

## Error Handling

The client provides typed error handling for various scenarios:

```typescript
try {
  await mdm.devices.getDevice(deviceId);
} catch (error) {
  if (error instanceof MDMAuthenticationError) {
    // Handle authentication failure
    console.log('Authentication failed:', error.message);
  } else if (error instanceof MDMRequestError) {
    // Handle API request error
    console.log('API error:', error.statusCode, error.message);
  } else {
    // Handle other errors
    console.error('Unexpected error:', error);
  }
}
```

## TypeScript Support

This library is written in TypeScript and provides comprehensive type definitions for all APIs. The types are automatically included when you install the package.

## Contributing

We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details.

## License

MIT

[build-img]: https://github.com/dovid-moshe-crow/manageengine-mdm/actions/workflows/release.yml/badge.svg
[build-url]: https://github.com/dovid-moshe-crow/manageengine-mdm/actions/workflows/release.yml
[downloads-img]: https://img.shields.io/npm/dt/manageengine-mdm
[downloads-url]: https://www.npmtrends.com/manageengine-mdm
[npm-img]: https://img.shields.io/npm/v/manageengine-mdm
[npm-url]: https://www.npmjs.com/package/manageengine-mdm
[issues-img]: https://img.shields.io/github/issues/dovid-moshe-crow/manageengine-mdm
[issues-url]: https://github.com/dovid-moshe-crow/manageengine-mdm/issues
[codecov-img]: https://codecov.io/gh/dovid-moshe-crow/manageengine-mdm/branch/main/graph/badge.svg
[codecov-url]: https://codecov.io/gh/dovid-moshe-crow/manageengine-mdm
[semantic-release-img]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
[semantic-release-url]: https://github.com/semantic-release/semantic-release
[commitizen-img]: https://img.shields.io/badge/commitizen-friendly-brightgreen.svg
[commitizen-url]: http://commitizen.github.io/cz-cli/
