npm package: @maximomize/maximo-api-client
Description: Third-party TypeScript client library for IBM Maximo Application Suite (MAS) with comprehensive REST API support for Manage API and Tools API
Author: Rana Ahmed Naazir
Latest version: 0.8.6

# Maximo API Client

[![npm version](https://img.shields.io/npm/v/%40maximomize%2Fmaximo-api-client.svg)](https://www.npmjs.com/package/@maximomize/maximo-api-client)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue.svg)](https://www.typescriptlang.org/)
[![Node.js](https://img.shields.io/badge/Node.js-%3E%3D14.0.0-green.svg)](https://nodejs.org/)

A comprehensive third-party TypeScript client library for IBM Maximo Application Suite (MAS) with full REST API support for both Manage API (OSLC/REST) and Tools API.

This package is distributed as compiled JavaScript with TypeScript declaration files. Source files and tests are not included in the published npm package.

## Features

- TypeScript support with full type safety
- Query building (selecting, filtering, sorting, paging)
- CRUD operations (create, read, update, delete)
- Resource-specific services for domain-focused operations
- Support for bulk operations
- Authentication handling
- **NEW**: Unified MAS API client supporting multiple Maximo APIs
- **NEW**: Tools API for administrative operations
- **NEW**: MMI service for Maximo Management Interface operations

## Installation

```bash
npm install @maximomize/maximo-api-client
```

### Requirements

- Node.js >= 14.0.0
- TypeScript >= 4.0 (for TypeScript projects)

## Usage

### Client Configuration (Builder Pattern)

The recommended way to create clients is using the Builder pattern, which provides a fluent API and better type safety.

#### Unified MAS Client (Recommended)

```typescript
import { MASClientBuilder } from '@maximomize/maximo-api-client';

const client = new MASClientBuilder()
  // Common settings only (timeouts, TLS, logger, etc.)
  .timeout(60000)
  .trustSelfSignedCerts()
  .configureManage(manage => manage
    .baseUrl('https://manage.example.com')
    .withApiKey('manage-api-key')
    .disableLeanMode()
  )
  .configureTools(tools => tools
    .baseUrl('https://tools.example.com')
    .apiKey('tools-api-key')
  )
  .build();
```

Use distinct base URLs for `manage` and `tools` when your MAS deployment exposes different hosts.

**Note on Defaults:**
- **SSL**: Enabled (`true`) by default.
- **Port**: Defaults to `443` if SSL is enabled, `80` otherwise.
- **ApiHome**: Defaults to `API` if using API Key, `OSLC` otherwise.

#### Standalone Maximo / Manage Client

```typescript
import { MaximoClientBuilder } from '@maximomize/maximo-api-client';

const client = new MaximoClientBuilder()
  .baseUrl('https://maximo.example.com')
  .withCredentials('username', 'password')
  .build();
```

### Legacy Configuration

You can still use the configuration object approach, though the builder is preferred.

```typescript
import { MaximoClient } from '@maximomize/maximo-api-client';

const client = new MaximoClient({
  baseUrl: 'https://your-maximo-instance.com/maximo',
  userName: 'username',
  password: 'password'
});
```

### Tools API

The Tools API provides administrative utilities for Maximo Manage:

```typescript
import { MASClientBuilder } from '@maximomize/maximo-api-client';

// Initialize Tools API client using the builder (Recommended)
const masClient = new MASClientBuilder()
  .configureManage(manage => manage
    .baseUrl('https://manage.example.com')
    .withApiKey('manage-api-key')
  )
  .configureTools(tools => tools
    .baseUrl('https://tools.example.com')
    .apiKey('your-tools-api-key')
  )
  .build();

const toolsClient = masClient.tools;

// Or standalone:
import { MaximoToolsClientBuilder } from '@maximomize/maximo-api-client';
const toolsClient = new MaximoToolsClientBuilder()
  .baseUrl('https://your-maximo-instance.com')
  .apiKey('your-tools-api-key')
  .build();

// Integrity checking
await toolsClient.integrityChecker.generateIntegrityCheckerLog();
const allLogs = await toolsClient.logManager.getAllToolsLogs();
const specificLog = await toolsClient.logManager.getToolsLog('logfile-name');

// Pod management
await toolsClient.podManager.stopPods();
await toolsClient.podManager.startPods();

// Database validation
await toolsClient.databaseService.validateDatabaseForMigration();

// Script execution
await toolsClient.scriptRunner.runDbcScript('V9000_05');

// ERD operations
await toolsClient.erdGenerator.generateErd();
const erd = await toolsClient.erdGenerator.viewErd();

// Build status
const buildStatus = await toolsClient.buildStatusChecker.getBuildStatus();

// Log management
await toolsClient.logManager.getAllToolsLogs();
```

### CRUD Operations

All resource services support both **Builder pattern** (fluent API with fine-grained control) and **Direct methods** (simple, concise calls). Below we use a generic object service instead of asset/workorder-specific APIs.

```typescript
const recordService = client.getGenericOslcService('mxapicustomobj');
```

#### GET Single Resource

| Approach | Method |
|----------|--------|
| **Builder** | `fetchById(id).properties(...).execute()` |
| **Direct** | `getById(id, select?)` or `findById(id, properties?)` |

```typescript
// Builder: Full control over request options
const record = await recordService
  .fetchById('12345')
  .properties(['customid', 'description', 'status'])
  .inlineDoc(true)
  .dropNulls(false)
  .execute();

// Direct: Simple and concise
const recordDirect = await recordService.getById('12345', ['customid', 'description']);
```

#### GET Multiple Resources

| Approach | Method |
|----------|--------|
| **Builder** | `query().where(...).select(...); executeQuery(query)` |
| **Direct** | `findAll(where?, select?, orderBy?)` or `findPaginated(...)` |

```typescript
// Builder: Full query capabilities
const query = recordService
  .query()
  .select(['customid', 'description', 'status'])
  .where('status="ACTIVE"')
  .orderBy('customid')
  .page(1, 50)
  .withCount();

const results = await recordService.executeQuery(query);

// Direct: Quick queries
const records = await recordService.findAll('status="ACTIVE"', ['customid', 'status']);
const page = await recordService.findPaginated('status="ACTIVE"', 1, 50, ['customid']);
```

#### CREATE Resource

| Approach | Method |
|----------|--------|
| **Builder** | `createResource(data).returnProperties(...).execute()` |
| **Direct** | `create(data, returnProperties?, getCreatedResult?, options?)` |

```typescript
// Builder: Control headers and response options
const newRecord = await recordService
  .createResource({ customid: 'C001', description: 'New Record', siteid: 'BEDFORD' })
  .returnProperties(['customid', 'status'])
  .withTransactionId('unique-tx-id')
  .suppressEvents()
  .execute();

// Direct: Simple creation
const newRecordDirect = await recordService.create(
  { customid: 'C001', description: 'New Record', siteid: 'BEDFORD' },
  ['customid', 'status']
);
```

#### UPDATE Resource

| Approach | Method |
|----------|--------|
| **Builder** | `updateResource(id, data).useMergePatch().execute()` |
| **Direct** | `update(id, data, returnProperties?, options?)` or `merge(...)` |

```typescript
// Builder: Full control including patch type
const result = await recordService
  .updateResource('12345', { description: 'Updated Description' })
  .useMergePatch()  // Use MERGE patch for child objects
  .returnProperties(['customid', 'description'])
  .suppressEvents()
  .execute();

// Direct: Simple update
await recordService.update('12345', { description: 'Updated Description' });

// Direct with MERGE (preserves unspecified child objects)
await recordService.merge('12345', {
  childcollection: [{ childid: 'LINE1', qty: 100 }]
});
```

#### DELETE Resource

| Approach | Method |
|----------|--------|
| **Builder** | `deleteById(id).suppressEvents().execute()` |
| **Direct** | `delete(idOrResource, options?)` |

```typescript
// Builder: Control deletion options
await recordService
  .deleteById('12345')
  .withTransactionId('tx-delete-123')
  .suppressEvents()
  .execute();

// Direct: Simple deletion
await recordService.delete('12345');
```

#### SYNC (Upsert) Resource

Creates if not exists, updates if exists (based on primary keys).

```typescript
// Direct method only (no builder)
await recordService.sync(
  { customid: 'C001', siteid: 'BEDFORD', description: 'Synced Record' },
  ['customid', 'status'],  // returnProperties
  'MERGE'  // patchType: 'MERGE' or 'UPDATE'
);
```

### Working with Automation Scripts (Built-in Custom Service)

`autoScript` is one of the built-in domain services in this library, alongside services such as `schema`, `scriptHandlerService`, and `getSystemService()`.

```typescript
// Get built-in automation script service
const autoscriptService = client.autoScript;

// Create a script
await autoscriptService.createScript(
  {
    autoscript: 'MXAPI_HELLO',
    language: 'python',
    source: 'service.log("Hello from MXAPI_HELLO")',
    description: 'Sample script created from API client',
    status: 'ACTIVE'
  },
  'autoscript,status,scriptlanguage'
);

// Find active scripts
const activeScripts = await autoscriptService.findByStatus('ACTIVE');

// Execute script handler endpoint
const result = await autoscriptService.execute('MXAPI_HELLO', { name: 'Maximo' });

// Update script source
await autoscriptService.uploadScript(
  'MXAPI_HELLO',
  'service.log("Updated source")',
  'autoscript,status'
);
```

### Other Built-in Services (System + Schema + More)

Other available built-in services on `MaximoClient` include:
- `asset`, `workOrder`, `location`
- `autoScript`, `scriptHandlerService`, `oslcInfoService`, `mmi`
- `schema`, `appXml`, `objectStructService`, `conditionExpression`, `toolsLog`
- `getSystemService()`, `getLoggingService()`, `getApiKeyService()`, `getConditionExpressionService()`

```typescript
// System service (method-based accessor)
const systemService = client.getSystemService();

const isManage = await systemService.isManage();
const serverDate = await systemService.getDate();
const authEnabled = await systemService.isOsSecurityOn();

// Schema service (getter accessor)
const schema = client.schema;
const assetSchema = await schema.getObjectStructureSchema(
  'mxapiasset',
  'properties,required,title'
);

// Manage Monitoring (MMI) APIs
const mmi = client.mmi;
const members = await mmi.getMembers();
const dbPing = await mmi.pingDb('thisserver');
const heapDump = await mmi.getAppServicesHeapDump('thisserver');

// API metadata / endpoint discovery
const oslcInfo = client.oslcInfoService;
const apiHome = await oslcInfo.fetchApiHomeDocument();

// Script handler endpoint service
const scriptHandler = client.scriptHandlerService;
const pong = await scriptHandler.executeScript('MXAPI_PING', {
  method: 'GET',
  queryParams: { ping: '1' }
});
```

### Working with Generic OSLC Services

Use `getGenericOslcService` (or `getService`) when there is no dedicated service class for an object structure.

```typescript
// Create a generic service for any object structure
const customObjectService = client.getGenericOslcService('mxapicustomobj');

// Query with full builder support
const query = customObjectService
  .query()
  .select(['customid', 'description', 'status'])
  .where('status="ACTIVE"')
  .orderBy('customid')
  .page(1, 20)
  .withCount();

const results = await customObjectService.executeQuery(query);
```

### Creating and Using a Custom Service

You can extend `ResourceService` for reusable business-specific methods and instantiate it through `client.getService(...)`.

```typescript
import {
  MaximoClient,
  HttpClient,
  OslcResource,
  ResourceService
} from '@maximomize/maximo-api-client';

interface InspectionForm extends OslcResource {
  inspformnum: string;
  description?: string;
  status?: string;
  siteid?: string;
}

class InspectionFormService extends ResourceService<InspectionForm> {
  constructor(httpClient: HttpClient, objectStructure: string = 'mxapiinspform') {
    super(httpClient, objectStructure);
  }

  async findActiveBySite(siteId: string): Promise<InspectionForm[]> {
    return this.findAll(`status="ACTIVE" and siteid="${siteId}"`);
  }
}

const client = new MaximoClient({
  baseUrl: 'https://your-maximo-instance.com',
  apiKey: 'your-api-key'
});

// Create your custom service instance via client factory
const inspectionFormService = client.getService<InspectionFormService>(
  'mxapiinspform',
  InspectionFormService
);

const forms = await inspectionFormService.findActiveBySite('BEDFORD');
```

## Contributing & Support

We have a [Public Repository](https://github.com/Maximomize/maximo-api-client-public) for:
- [Reporting Issues & Bugs](https://github.com/Maximomize/maximo-api-client-public/issues)
- Feature requests
- [Documentation](https://maximomize.github.io/maximo-api-client-public/)


## License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.