Example: Batch create documents
This example demonstrates how the Kameleon API can be used to traverse document template categories defined within Kameleon, and batch-create and save documents locally while preserving the same category structure.
Node.js + TypeScript
Following environment variables are used in example
TENANT_ID,CLIENT_IDandCLIENT_SECRETAzure AD credentials used for authentication.Refer to https://learn.microsoft.com/en-us/entra/identity-platform/how-to-add-credentials?tabs=client-secret for setup instructions.
API_URLhttps://api.kameleon.appAPI_SCOPEscope for Kameleon APIapi://e95a233a-a13e-4c5d-99b9-51832e41e036/.defaultAUTHOR_NAMEname of the document author e.g John DoeCOMPANY_IDcompany identifier retrieved from the Kameleon API.UNIT_IDcompany unit identifier retrieved from the Kameleon API.
Full example
This example uses client secret to acquire access token for Kameleon API calls.
import { AccessToken, ClientSecretCredential } from "@azure/identity";
import { mkdir, writeFile } from 'fs/promises'
import { join } from 'path'
interface IEntity {
id: string
name: string
}
interface IDocument extends IEntity {
extension: string
}
interface IDocumentCategory extends IEntity {
contents: IDocument[]
subcategories: IEntity[]
}
const sanitizePathname = (pathname: string) => pathname.replace(/[/\\?%*:|"<>]/g, '-')
async function request(accessToken: AccessToken, path: string, body?: any) {
const url = new URL(path, process.env.API_URL)
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
'Authorization': `${accessToken.tokenType} ${accessToken.token}`
},
method: body ? 'POST' : 'GET',
body: JSON.stringify(body)
})
if (!response.ok) {
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}
return response
}
async function generateDocuments() {
// Get access token
const { TENANT_ID, CLIENT_ID, CLIENT_SECRET, API_SCOPE } = process.env
const credential = new ClientSecretCredential(TENANT_ID!, CLIENT_ID!, CLIENT_SECRET!)
const accessToken = await credential.getToken(API_SCOPE!)
// Get document root categories
const response = await request(accessToken, '/v1.0/document-categories')
const categories: IEntity[] = await response.json()
for (const category of categories) {
await processCategory(accessToken, category, './documents')
}
}
async function processCategory(accessToken: AccessToken, entity: IEntity, path: string): Promise<void> {
// Get full category (contains documents and subcategories)
const response = await request(accessToken, `/v1.0/document-categories/${entity.id}`)
const category: IDocumentCategory = await response.json()
const categoryPath = join(path, sanitizePathname(category.name))
await mkdir(categoryPath, { recursive: true })
// Generate documents
for (const document of category.contents) {
await createDocument(accessToken, document, categoryPath)
}
// Process subcategories (recursive)
for (const subcategory of category.subcategories) {
await processCategory(accessToken, subcategory, categoryPath)
}
}
async function createDocument(accessToken: AccessToken, document: IDocument, path: string): Promise<void> {
const pathname = join(path, `${sanitizePathname(document.name)}.${document.extension}`)
console.log(`Generating '${pathname}'...`)
// Generate document binary
const { AUTHOR_NAME, COMPANY_ID, UNIT_ID } = process.env
const response = await request(accessToken, `/v1.0/documents/${document.id}/create`, {
author: {
name: AUTHOR_NAME,
companyId: COMPANY_ID, // See "/v1.0/companies" endpoint
unitId: UNIT_ID // See "/v1.0/companies/{id}" endpoint
}
})
// Save document locally
await writeFile(pathname, await response.bytes())
}
generateDocuments()