# Agility CMS & Management API TypeScript SDK

## Table of Contents

- [About the Management API SDK](#about-the-management-api-sdk)
- [Getting Started](#getting-started)
- [Authentication & Setup](./docs/auth.md)
- [API Method Classes](#api-method-classes)
- [Examples](#examples)
- [TypeScript Interfaces](#typescript-interfaces)
- [Available Enums](#available-enums)

---

## About the Management API SDK

- Provides a facility to developers to use the new Agility Management API more effectively.
- Provides methods to perform operations on Assets, Batches, Containers, Content, Models, Pages, and Users.
- **Content and Page Creation** - Create individual or multiple content items and pages (which automatically get added to batches).
- **Essential batch workflow operations** - Perform publish, unpublish, approve, decline, and request approval operations on existing batches.
- Ability to generate Content in bulk for a Website.
- **Strongly typed TypeScript interfaces** for all batch operations with comprehensive error handling.

## Getting Started

### Prerequisites
1. Clone the repository agility-cms-management-sdk-typescript.
2. Import the index file to make use of the Options class.
3. You will need valid Agility CMS credentials to authenticate and obtain an access token.

### Authentication
For detailed authentication instructions, including OAuth 2.0 setup, token management, and security best practices, see the **[Authentication & Setup Guide](./docs/auth.md)**.

### Making a Request

#### Simple OAuth Authentication (Recommended)

1. First, initiate the authorization flow by making a GET request to the authorization endpoint:
```javascript
const authUrl = 'https://mgmt.aglty.io/oauth/authorize';

//if you wish to implement offline access using refresh tokens, use this URL (enables refresh tokens)
//const authUrl = 'https://mgmt.aglty.io/oauth/authorize?scope=offline-access '; 

const params = new URLSearchParams({
  response_type: 'code',
  redirect_uri: 'YOUR_REDIRECT_URI',
  state: 'YOUR_STATE',
  scope: 'openid profile email offline_access'
});

// Redirect the user to the authorization URL
window.location.href = `${authUrl}?${params.toString()}`;
```

2. After successful authentication, you'll receive an authorization code at your redirect URI. Use this code to obtain an access token:
```javascript
const response = await fetch('https://mgmt.aglty.io/oauth/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  body: new URLSearchParams({
    code: 'YOUR_AUTHORIZATION_CODE'
  })
});

const { access_token, refresh_token, expires_in } = await response.json();
```

3. Use the obtained token to initialize the SDK:
```javascript
import * as mgmtApi from "@agility/management-sdk";

// Initialize the Options Class with your authentication token
let options = new mgmtApi.Options();
options.token = access_token; // Use the token obtained from authentication

// Initialize the APIClient Class
let apiClient = new mgmtApi.ApiClient(options);

let guid = "<<Provide the Guid of the Website>>";
let locale = "<<Provide the locale of the Website>>"; // Example: en-us

// Now you can make authenticated requests
var contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
console.log(JSON.stringify(contentItem));
```

4. When the access token expires, use the refresh token to obtain a new access token:
```javascript
const response = await fetch('https://mgmt.aglty.io/oauth/refresh', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    refresh_token: 'YOUR_REFRESH_TOKEN'
  })
});

const { access_token, refresh_token, expires_in } = await response.json();
```


#### Manual Token Authentication (For CI/CD)
```typescript
import * as mgmtApi from "@agility/management-sdk";

// Token-based approach for CI/CD and automated environments
const options = new mgmtApi.Options();
options.token = process.env.AGILITY_API_TOKEN;
const apiClient = new mgmtApi.ApiClient(options);

const guid = process.env.AGILITY_GUID;
const locale = "en-us";

const contentItem = await apiClient.contentMethods.getContentItem(22, guid, locale);
console.log('Content retrieved:', contentItem.fields.title);
```

> 💡 **Note**: The new OAuth authentication requires `keytar` for secure token storage. See the [Authentication & Setup Guide](./docs/auth.md) for OAuth setup or [CI/CD & Automated Environments](./docs/cicd.md) for token-based authentication.

## API Method Classes

### File and Media Management
- **[AssetMethods](./docs/asset-methods.md)** - File and media management operations (15 functions)
  - [deleteFile](./docs/asset-methods.md#deletefile), [moveFile](./docs/asset-methods.md#movefile), [getMediaList](./docs/asset-methods.md#getmedialist), [getGalleries](./docs/asset-methods.md#getgalleries), [getGalleryById](./docs/asset-methods.md#getgallerybyid), [getGalleryByName](./docs/asset-methods.md#getgallerybyname), [getDefaultContainer](./docs/asset-methods.md#getdefaultcontainer), [saveGallery](./docs/asset-methods.md#savegallery), [deleteGallery](./docs/asset-methods.md#deletegallery), [getAssetByID](./docs/asset-methods.md#getassetbyid), [getAssetByUrl](./docs/asset-methods.md#getassetbyurl), [upload](./docs/asset-methods.md#upload), [createFolder](./docs/asset-methods.md#createfolder), [deleteFolder](./docs/asset-methods.md#deletefolder), [renameFolder](./docs/asset-methods.md#renamefolder)

### Workflow Operations
- **[BatchMethods](./docs/batch-methods.md)** - Batch workflow operations (7 functions)
  - [getBatch](./docs/batch-methods.md#getbatch), [publishBatch](./docs/batch-methods.md#publishbatch), [unpublishBatch](./docs/batch-methods.md#unpublishbatch), [approveBatch](./docs/batch-methods.md#approvebatch), [declineBatch](./docs/batch-methods.md#declinebatch), [requestApprovalBatch](./docs/batch-methods.md#requestapprovalbatch), [getBatchTypes](./docs/batch-methods.md#getbatchtypes)

### Content Management
- **[ContainerMethods](./docs/container-methods.md)** - Container management operations (8 functions)
  - [getContainerByID](./docs/container-methods.md#getcontainerbyid), [getContainersByModel](./docs/container-methods.md#getcontainersbymodel), [getContainerByReferenceName](./docs/container-methods.md#getcontainerbyreferencename), [getContainerSecurity](./docs/container-methods.md#getcontainersecurity), [getContainerList](./docs/container-methods.md#getcontainerlist), [getNotificationList](./docs/container-methods.md#getnotificationlist), [saveContainer](./docs/container-methods.md#savecontainer), [deleteContainer](./docs/container-methods.md#deletecontainer)

- **[ContentMethods](./docs/content-methods.md)** - Content item operations (14 functions)
  - [getContentItem](./docs/content-methods.md#getcontentitem), [publishContent](./docs/content-methods.md#publishcontent), [unPublishContent](./docs/content-methods.md#unpublishcontent), [batchWorkflowContent](./docs/content-methods.md#batchworkflowcontent), [contentRequestApproval](./docs/content-methods.md#contentrequestapproval), [approveContent](./docs/content-methods.md#approvecontent), [declineContent](./docs/content-methods.md#declinecontent), [deleteContent](./docs/content-methods.md#deletecontent), [saveContentItem](./docs/content-methods.md#savecontentitem), [saveContentItems](./docs/content-methods.md#savecontentitems), [getContentItems](./docs/content-methods.md#getcontentitems), [getContentList](./docs/content-methods.md#getcontentlist), [getContentHistory](./docs/content-methods.md#getcontenthistory), [getContentComments](./docs/content-methods.md#getcontentcomments)

- **[ModelMethods](./docs/model-methods.md)** - Content model operations (6 functions)
  - [getContentModel](./docs/model-methods.md#getcontentmodel), [getModelByReferenceName](./docs/model-methods.md#getmodelbyreferencename), [getContentModules](./docs/model-methods.md#getcontentmodules), [getPageModules](./docs/model-methods.md#getpagemodules), [saveModel](./docs/model-methods.md#savemodel), [deleteModel](./docs/model-methods.md#deletemodel)

### Page Management
- **[PageMethods](./docs/page-methods.md)** - Page management operations (18 functions)
  - [getPage](./docs/page-methods.md#getpage), [getPageByPath](./docs/page-methods.md#getpagebypath), [getPageHistory](./docs/page-methods.md#getpagehistory), [getPageComments](./docs/page-methods.md#getpagecomments), [getPageList](./docs/page-methods.md#getpagelist), [getPageListByPageTemplateID](./docs/page-methods.md#getpagelistbypagetemplateid), [getPageListByPage](./docs/page-methods.md#getpagelistbypage), [getPageListByPageAndPageTemplateID](./docs/page-methods.md#getpagelistbypageandpagetemplateid), [getPageTree](./docs/page-methods.md#getpagetree), [getPageTemplateList](./docs/page-methods.md#getpagetemplatelist), [getPageSecurity](./docs/page-methods.md#getpagesecurity), [getPageItemTemplateList](./docs/page-methods.md#getpageitemtemplatelist), [getPageContentZones](./docs/page-methods.md#getpagecontentzones), [savePage](./docs/page-methods.md#savepage), [savePageSecurity](./docs/page-methods.md#savepagesecurity), [movePageItem](./docs/page-methods.md#movepageitem), [deletePage](./docs/page-methods.md#deletepage), [batchWorkflowPages](./docs/page-methods.md#batchworkflowpages)

### User Management
- **[InstanceMethods](./docs/instance-methods.md)** - Instance-level operations (2 functions)
  - [getLocales](./docs/instance-methods.md#getlocales), [getFetchApiStatus](./docs/instance-methods.md#getfetchapistatus)

- **[InstanceUserMethods](./docs/instance-user-methods.md)** - Instance user management (3 functions)
  - [getUsers](./docs/instance-user-methods.md#getusers), [saveUser](./docs/instance-user-methods.md#saveuser), [deleteUser](./docs/instance-user-methods.md#deleteuser)

- **[ServerUserMethods](./docs/server-user-methods.md)** - Server user operations (2 functions)
  - [me](./docs/server-user-methods.md#me), [you](./docs/server-user-methods.md#you)

### Integration
- **[WebhookMethods](./docs/webhook-methods.md)** - Webhook management (4 functions)
  - [getWebhook](./docs/webhook-methods.md#getwebhook), [webhookList](./docs/webhook-methods.md#webhooklist), [saveWebhook](./docs/webhook-methods.md#savewebhook), [deleteWebhook](./docs/webhook-methods.md#deletewebhook)

### Multi-Instance Operations
- **[Multi-Instance Operations](./docs/multi-instance-operations.md)** - Advanced workflows for managing content across multiple instances and locales
  - Configuration-driven content creation across multiple environments
  - Parallel processing and batch operations
  - Performance optimization and error handling
  - Cross-instance synchronization and reporting

### CI/CD & Automation
- **[CI/CD & Automated Environments](./docs/cicd.md)** - Token-based authentication for automated environments
  - CI/CD pipeline examples (GitHub Actions, GitLab CI, Jenkins)
  - Serverless functions (AWS Lambda, Vercel)
  - Environment configuration and security best practices
  - Token management and rotation

## Examples

### Understanding Agility CMS Batch Architecture

Agility CMS uses a simple approach for working with content and batches:

#### Content/Page Creation (Handled by Respective Controllers)
- **contentMethods.saveContentItem()** - Creates a single new content item and adds it to a batch
- **contentMethods.saveContentItems()** - Creates multiple new content items and adds them to a batch  
- **pageMethods.savePage()** - Creates a new page and adds it to a batch
- These methods create the content AND automatically handle batch creation/management

#### Batch Workflow Operations (Handled by Batch Controller)
- **batchMethods.publishBatch()** - Publishes all items in an existing batch
- **batchMethods.unpublishBatch()** - Unpublishes all items in an existing batch
- **batchMethods.approveBatch()** - Approves all items in an existing batch
- **batchMethods.declineBatch()** - Declines all items in an existing batch
- **batchMethods.requestApprovalBatch()** - Requests approval for all items in an existing batch
- **batchMethods.getBatch()** - Retrieves details of an existing batch
- **batchMethods.getBatchTypes()** - Retrieves all batch-related enum types for developer discovery

### Complete Workflow Example

```typescript
import * as mgmtApi from '@agility/management-sdk';

// Authenticate with simple OAuth flow
const client = new mgmtApi.ApiClient();
await client.auth();

const guid = 'your-instance-guid';
const locale = 'en-us';

// Get batch types for dynamic UI and validation
const batchTypes = await client.batchMethods.getBatchTypes(guid);
console.log('Available batch types loaded:', batchTypes);

// Create NEW content items (handled by content controller)
// This automatically creates and manages batches
const newContentBatchID = await client.contentMethods.saveContentItem(locale, {
    properties: {
        referenceName: 'blog-post',
        definitionName: 'BlogPost',
        state: 2
    },
    fields: {
        title: 'My New Blog Post',
        content: 'This is the blog post content...'
    }
});

// Create NEW page (handled by page controller)  
// This automatically creates and manages batches
const newPageBatchID = await client.pageMethods.savePage(locale, {
    name: 'new-product-page',
    title: 'New Product Page',
    menuText: 'New Product',
    pageType: 'static',
    templateName: 'Product Template',
    parentPageID: -1
});

// Retrieve details of an existing batch
const batchDetails = await client.batchMethods.getBatch(newContentBatchID, guid);
console.log('Batch details:', batchDetails);

// Perform workflow operations on existing batches
await client.batchMethods.publishBatch(newContentBatchID, guid);
await client.batchMethods.approveBatch(newPageBatchID, guid);

// Or use immediate return for custom polling
const batchId = await client.batchMethods.publishBatch(newContentBatchID, guid, true);
console.log('Batch submitted for publishing:', batchId);

console.log('All operations completed successfully!');
```

### Batch Workflow Operations

Perform workflow operations (publish, unpublish, approve, decline, request approval) on multiple content items or pages in a single operation using the `WorkflowOperationType` enum:

```typescript
import * as mgmtApi from '@agility/management-sdk';
import { WorkflowOperationType } from '@agility/management-sdk';

const options = new mgmtApi.Options();
options.token = 'your-access-token';
const client = new mgmtApi.ApiClient(options);

const guid = 'your-instance-guid';
const locale = 'en-us';

// Batch publish multiple content items
const contentIDs = [101, 102, 103];
const publishedContentIDs = await client.contentMethods.batchWorkflowContent(
    contentIDs, guid, locale, WorkflowOperationType.Publish
);
console.log('Published content IDs:', publishedContentIDs);

// Batch unpublish multiple content items
const unpublishedContentIDs = await client.contentMethods.batchWorkflowContent(
    contentIDs, guid, locale, WorkflowOperationType.Unpublish
);
console.log('Unpublished content IDs:', unpublishedContentIDs);

// Batch approve multiple content items
const approvedContentIDs = await client.contentMethods.batchWorkflowContent(
    contentIDs, guid, locale, WorkflowOperationType.Approve
);
console.log('Approved content IDs:', approvedContentIDs);

// Batch publish multiple pages
const pageIDs = [201, 202, 203];
const publishedPageIDs = await client.pageMethods.batchWorkflowPages(
    pageIDs, guid, locale, WorkflowOperationType.Publish
);
console.log('Published page IDs:', publishedPageIDs);

// Batch unpublish multiple pages
const unpublishedPageIDs = await client.pageMethods.batchWorkflowPages(
    pageIDs, guid, locale, WorkflowOperationType.Unpublish
);
console.log('Unpublished page IDs:', unpublishedPageIDs);

// Get batch ID immediately for custom polling (optional 5th parameter)
const [batchId] = await client.contentMethods.batchWorkflowContent(
    contentIDs, guid, locale, WorkflowOperationType.Publish, true
);
console.log('Batch submitted, ID:', batchId);
```

### Checking Fetch API Sync Status

After batch operations, you may want to verify that changes have propagated to the Fetch API CDN before using the content on your frontend:

```typescript
import * as mgmtApi from '@agility/management-sdk';

const options = new mgmtApi.Options();
options.token = 'your-access-token';
const client = new mgmtApi.ApiClient(options);

const guid = 'your-instance-guid';

// After a batch publish operation, wait for CDN sync
await client.batchMethods.publishBatch(batchId, guid);

// Wait for sync to complete
await client.instanceMethods.getFetchApiStatus(guid, 'fetch', true);
console.log('Sync complete! Content is now available on the CDN');

// Or just check current status without waiting
const status = await client.instanceMethods.getFetchApiStatus(guid);
console.log('Sync in progress:', status.inProgress);
```

## TypeScript Interfaces

### Batch Response Interfaces

```typescript
interface BatchTypesResponse {
    itemTypes: EnumInfo[];         // Page, ContentItem, ContentList, Tag, ModuleDef
    operationTypes: EnumInfo[];    // All operation types 
    workflowOperations: EnumInfo[]; // Publish, Unpublish, Approve, Decline, RequestApproval
    states: EnumInfo[];            // None, Pending, InProcess, Processed, Deleted
}

interface EnumInfo {
    value: number;      // Numeric enum value
    name: string;       // String name (e.g., "Publish")
    description: string; // Human-readable description
}
```

## Available Enums

### WorkflowOperationType (for batch processing)
> **Note**: In the API documentation (Swagger UI), these operations appear as descriptive dropdown options (Publish, Unpublish, Approve, Decline, RequestApproval) instead of numbers.

- `Publish = 1`
- `Unpublish = 2` 
- `Approve = 3`
- `Decline = 4`
- `RequestApproval = 5`

### BatchItemType
> **Note**: In the API documentation (Swagger UI), these appear as descriptive dropdown options (Page, ContentItem, ContentList, Tag, ModuleDef) instead of numbers.

- `Page = 1`
- `ContentItem = 2`
- `ContentList = 3`
- `Tag = 4`
- `ModuleDef = 5`

### BatchState
> **Note**: In the API documentation (Swagger UI), these appear as descriptive names (None, Pending, InProcess, Processed, Deleted) instead of numbers.

- `None = 0` - Batch created but not submitted for processing
- `Pending = 1` - Batch is pending processing
- `InProcess = 2` - Batch is currently being processed  
- `Processed = 3` - Batch has been processed successfully
- `Deleted = 4` - Batch has been deleted

## Error Handling

All methods throw exceptions on failure:

```typescript
try {
    const batch = await client.batchMethods.getBatch(123, 'instance-guid');
} catch (error) {
    console.error('Failed to get batch:', error.message);
}
```

## License

MIT

If you have feedback or questions about this starter, please use the [Github Issues](https://github.com/agility/agility-cms-management-sdk-typescript/issues) on this repo, join our [Community Slack Channel](https://join.slack.com/t/agilitycommunity/shared_invite/enQtNzI2NDc3MzU4Njc2LWI2OTNjZTI3ZGY1NWRiNTYzNmEyNmI0MGZlZTRkYzI3NmRjNzkxYmI5YTZjNTg2ZTk4NGUzNjg5NzY3OWViZGI) or create a post on the [Agility Developer Community](https://help.agilitycms.com/hc/en-us/community/topics).
