Skip to main content
Skip table of contents

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

Full example

This example uses client secret to acquire access token for Kameleon API calls.

TYPESCRIPT
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()
JavaScript errors detected

Please note, these errors can depend on your browser setup.

If this problem persists, please contact our support.