0.7.4 • Published 5 months ago

@panoptic-it-solutions/autotask-client v0.7.4

Weekly downloads
-
License
AGPL-3.0-or-later
Repository
github
Last release
5 months ago

@panoptic-it-solutions/autotask-client

A Node.js SDK for interacting with the Autotask REST API, featuring built-in rate limiting, TypeScript support, automatic pagination, and relationship embedding.

Table of Contents

Installation

npm install @panoptic-it-solutions/autotask-client
# or
yarn add @panoptic-it-solutions/autotask-client
# or
pnpm add @panoptic-it-solutions/autotask-client

Configuration

This SDK requires the following environment variables to be set for basic Autotask API interaction:

  • AUTOTASK_USERNAME: Your Autotask API User's username (Access Key in Autotask).
  • AUTOTASK_SECRET: Your Autotask API User's secret (Secret Key in Autotask).
  • AUTOTASK_API_INTEGRATION_CODE: Your Autotask API Tracking Identifier.
  • AUTOTASK_API_URL: The base URL for the Autotask REST API specific to your zone (e.g., https://webservices[XX].autotask.net/ATServicesRest/v1.0/). Ensure this ends with a trailing slash.

Upstash Redis for Rate Limiting (Recommended)

For robust, distributed rate limiting (essential for serverless environments or horizontally scaled applications), the client integrates with Upstash Redis.

Required Environment Variables for Upstash Rate Limiting:

  • UPSTASH_REDIS_REST_URL: The REST URL for your Upstash Redis instance.
  • UPSTASH_REDIS_REST_TOKEN: The read/write token for your Upstash Redis instance.

You can find these credentials in your Upstash console after creating a Redis database.

Optional Upstash Rate Limit Configuration (via AutotaskClientConfig):

Default rate limits (e.g., 5 requests per second, 5000 per hour) can be overridden in the AutotaskClientConfig (see Client Instantiation). Environment variables UPSTASH_HOURLY_LIMIT and UPSTASH_SECONDLY_LIMIT are no longer directly used for overriding these; use the config object instead.

If Upstash credentials are not provided in the config, the client will operate without proactive distributed rate limiting, relying solely on reactive backoff for Autotask API 429 responses. This is not recommended for production or distributed environments.

Example .env File

It's recommended to use a library like dotenv to manage these variables. Create a .env file in the root of your project:

# Autotask API Credentials
AUTOTASK_USERNAME="YOUR_API_USERNAME"
AUTOTASK_SECRET="YOUR_API_SECRET"
AUTOTASK_API_INTEGRATION_CODE="YOUR_API_INTEGRATION_CODE"
AUTOTASK_API_URL="https://your-zone.autotask.net/ATServicesRest/v1.0/"

# Upstash Redis Credentials (Recommended for Rate Limiting)
UPSTASH_REDIS_REST_URL="your_upstash_redis_url_here"
UPSTASH_REDIS_REST_TOKEN="your_upstash_redis_token_here"

Load these variables at the beginning of your application:

import * as dotenv from "dotenv";
dotenv.config();

Usage

Client Instantiation

import {
  AutotaskClient,
  AutotaskClientConfig,
  LogLevel, // For setting log levels
} from "@panoptic-it-solutions/autotask-client";
import * as dotenv from "dotenv";

// Load environment variables
dotenv.config();

const clientConfig: AutotaskClientConfig = {
  userName: process.env.AUTOTASK_USERNAME!,
  secret: process.env.AUTOTASK_SECRET!,
  integrationCode: process.env.AUTOTASK_API_INTEGRATION_CODE!,
  apiBaseUrl: process.env.AUTOTASK_API_URL!, // Renamed from baseUrl
  // Optional Upstash configuration for rate limiting
  upstash: {
    upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL, // Can be undefined if not using Upstash
    upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN, // Can be undefined
    // hourlyLimit: 4500, // Example: Override default hourly limit
    // secondlyLimit: 4,    // Example: Override default per-second limit
  },
  // Optional retry configuration (client has sensible defaults)
  retryConfig: {
    maxRetries: 5,
    initialBackoffMs: 1000,
  },
  // Optional logging configuration
  logLevel: LogLevel.INFO, // Supported levels: DEBUG, INFO, WARN, ERROR, NONE
  // logger: new MyCustomLogger(), // Optionally provide a custom logger instance
};

const client = new AutotaskClient(clientConfig);

Querying a Single Page of Entities (queryEntities)

Use queryEntities to fetch a single page of entities.

import {
  AutotaskQueryOptions,
  AllAutotaskEntityNames,
  AutotaskEntityTypeMap,
  SdkClientPagedResponse,
} from "@panoptic-it-solutions/autotask-client";

// Assume 'client' is an instantiated AutotaskClient

async function getFirstPageOfActiveCompanies() {
  try {
    const entityName: AllAutotaskEntityNames = "Companies";
    const options: AutotaskQueryOptions = {
      fields: ["id", "companyName", "companyType", "isActive"],
      filter: [
        // Refer to Autotask API docs for correct field name (e.g., 'status' or 'companyStatus') and value
        { field: "isActive", op: "eq", value: true },
      ],
      sdkPageSize: 10,
      includeTotalCount: true, // Get total count for pagination info on the first request
    };

    const response: SdkClientPagedResponse<AutotaskEntityTypeMap["Companies"]> =
      await client.queryEntities(entityName, options);

    console.log(
      `Fetched page ${response.currentPage} of ${
        response.totalPages ?? "unknown"
      }, containing ${response.items.length} companies.`
    );
    if (response.totalItemsAvailable !== undefined) {
      console.log(
        `Total active companies matching filter: ${response.totalItemsAvailable}`
      );
    }

    response.items.forEach((company) => {
      console.log(
        `- ID: ${company.id}, Name: ${company.companyName}, Type: ${company.companyType}, Active: ${company.isActive}`
      );
    });

    if (response.hasNextPage && response.continuationToken) {
      console.log(
        "\nTo fetch the next page, use this continuationToken:",
        response.continuationToken
      );
      // Example: Fetching next page
      // const nextPageOptions: AutotaskQueryOptions = {
      //   continuationToken: response.continuationToken,
      //   sdkPageSize: 10 // sdkPageSize can be repeated or omitted (uses original if omitted)
      // };
      // const nextPageResponse = await client.queryEntities(entityName, nextPageOptions);
    }
  } catch (error) {
    console.error("Error fetching companies:", error);
    // Handle specific errors, e.g., AutotaskApiError, RateLimitError
  }
}

getFirstPageOfActiveCompanies();

Fetching All Pages Automatically (queryAllEntities)

Use queryAllEntities to retrieve all items matching a query, with the SDK handling pagination transparently.

import {
  AutotaskQueryOptions,
  AllAutotaskEntityNames,
} from "@panoptic-it-solutions/autotask-client";
// Assume 'client' is an instantiated AutotaskClient

async function fetchAllActiveContacts() {
  try {
    const entityName: AllAutotaskEntityNames = "Contacts";
    const options: AutotaskQueryOptions = {
      filter: [{ field: "isActive", op: "eq", value: true }],
      fields: ["id", "firstName", "lastName", "emailAddress"],
      sdkPageSize: 50, // SDK fetches in chunks of 50 from the API
    };

    console.log("Fetching all active contacts...");
    const allContacts = await client.queryAllEntities(
      entityName,
      options,
      // Optional progress callback
      (progress) => {
        const total =
          progress.totalKnownItems !== undefined
            ? ` of ${progress.totalKnownItems}`
            : "";
        console.log(
          `Progress: Page ${progress.currentPageNumber} fetched. ` +
            `${progress.totalItemsFetchedSoFar}${total} contacts retrieved.`
        );
      },
      // Optional: fetch total count upfront for more accurate progress reporting
      { fetchTotalCountForProgress: true }
    );

    console.log(`Successfully fetched ${allContacts.length} active contacts.`);
    // Process allContacts array
  } catch (error) {
    console.error("Failed to fetch all contacts:", error);
  }
}

fetchAllActiveContacts();

Filtering

The SDK supports powerful filtering capabilities when querying entities.

Filter Structure (AutotaskApiFilter)

Filters are provided as an array of AutotaskApiFilter objects to the filter property in AutotaskQueryOptions. Each filter object has the following structure:

interface AutotaskApiFilter {
  field: string;
  op: AutotaskApiFilterOp; // Union of supported operator strings
  value: string | number | boolean | (string | number)[]; // Value depends on 'op'
  udf?: boolean; // Set to true if 'field' is a User-Defined Field
}
  • field: The camelCase name of the field to filter on (e.g., companyName, status, lastActivityDate). Always consult the official Autotask REST API documentation for the correct filterable field names for each entity.
  • op: The filter operator. See Available Filter Operators.
  • value: The value to compare against. For the 'in' or 'notin' operators, this should be an array (e.g., [1, 2, 3] or ['Val1', 'Val2']).
  • udf: (Optional) Boolean, set to true if the field is a User-Defined Field (UDF).

Multiple filters in the array are combined with AND logic.

Available Filter Operators (AutotaskApiFilterOp)

The AutotaskApiFilterOp type includes the following string literals. Refer to the Autotask REST API documentation for the definitive list of supported operators for specific fields and entities, as not all operators apply to all fields.

OperatorDescriptionExample Value(s)Notes
eqEquals1, "Active", true
noteqNot equals1, "Active", trueMatches noteq in type def
gtGreater than100, "2023-01-01T00:00:00Z"For numbers, dates
gteGreater than or equal to100, "2023-01-01"For numbers, dates
ltLess than100, "2023-12-31"For numbers, dates
lteLess than or equal to100, "2023-12-31"For numbers, dates
beginsWithStarts with"Tech"Case-insensitive for strings
endsWithEnds with"Solutions"Case-insensitive for strings
containsContains (substring match)"Service"Case-insensitive for strings
existField is not null(value not applicable)
notExistField is null(value not applicable)
inIn a list of values[1, 2, 3], ["Val1", "Val2"]value is an array
notInNot in a list of values[4, 5]value is an array

Important: Always cross-reference with the Autotask API documentation for the most accurate and up-to-date list of supported operators and their behavior for specific entity fields. The SDK's AutotaskQueryOperator type provides type safety for the operators listed above; the API may support additional query capabilities.

Filter Examples

// Active companies created this year
const recentActiveCompanies = await client.queryEntities("Companies", {
  filter: [
    { field: "isActive", op: "eq", value: true },
    { field: "createDate", op: "isThisYear" },
  ],
  fields: ["id", "companyName", "createDate"],
});

// Tickets with high priority, not yet completed
const highPriorityOpenTickets = await client.queryEntities("Tickets", {
  filter: [
    { field: "priority", op: "eq", value: 1 }, // Assuming 1 is High Priority
    { field: "status", op: "notin", value: [5, 6] }, // Assuming 5=Complete, 6=Cancelled
    { field: "assignedResourceID", op: "isnotnull" }, // Must be assigned
  ],
  fields: ["id", "ticketNumber", "title", "priority", "status"],
});

Pagination

The queryEntities method uses a continuation token-based system.

Pagination Options

In AutotaskQueryOptions:

  • sdkPageSize?: number: Desired items per SDK page (default: 25). The SDK may make multiple API calls to fulfill this.
  • continuationToken?: string: Token from a previous response to fetch the next/previous page.
  • pageRequest?: 'next' | 'prev': Direction when using continuationToken. Default is 'next'.
  • includeTotalCount?: boolean: If true (and no continuationToken), makes an extra call for total count. Default: false.

When using continuationToken, the filter and fields from the original query are implicitly used. Re-specifying them might lead to unpredictable behavior.

Response Properties

SdkClientPagedResponse<T> contains:

  • items: T[]: Entities for the current page.
  • pageSize: number: sdkPageSize used.
  • currentPage: number: 1-indexed SDK page number.
  • continuationToken: string | null: Token for navigation. Null if no more pages in that direction.
  • hasNextPage: boolean: If a next page is available.
  • hasPreviousPage: boolean: If a previous page is available.
  • totalItemsAvailable?: number: Total items matching (if includeTotalCount was true on initial query).
  • totalPages?: number: Total SDK pages (if totalItemsAvailable known).
  • errors?: AutotaskErrorItem[]: API errors from the response body.

Example: Manual Pagination Loop

// Assume 'client' is an instantiated AutotaskClient
async function getAllActiveCompaniesManually() {
  let allCompanies: AutotaskEntityTypeMap["Companies"][] = [];
  let currentContinuationToken: string | null = null;
  const sdkPageSize = 10;
  let currentPageNumber = 0;
  let totalItems: number | undefined;
  let totalSdkPages: number | undefined;
  const entityName: AllAutotaskEntityNames = "Companies";

  console.log(`Fetching active companies, ${sdkPageSize} per SDK page...`);

  try {
    do {
      currentPageNumber++;
      const queryOptions: AutotaskQueryOptions = {
        ...(currentContinuationToken
          ? { continuationToken: currentContinuationToken } // Subsequent requests only need token
          : {
              // First request
              fields: ["id", "companyName", "isActive"],
              filter: [{ field: "isActive", op: "eq", value: true }],
              includeTotalCount: true,
            }),
        sdkPageSize: sdkPageSize,
      };

      console.log(
        `Requesting SDK page ${currentPageNumber}${
          totalSdkPages ? ` of ${totalSdkPages}` : ""
        }...`
      );
      const response = await client.queryEntities(entityName, queryOptions);

      if (response.errors && response.errors.length > 0) {
        console.error("Errors received:", response.errors);
        break;
      }

      if (currentPageNumber === 1) {
        // Capture totals from the first response
        totalItems = response.totalItemsAvailable;
        totalSdkPages = response.totalPages;
      }

      const pageInfo = totalSdkPages
        ? `SDK Page ${response.currentPage}/${totalSdkPages}`
        : `SDK Page ${response.currentPage}`;
      console.log(`  Received ${pageInfo}: ${response.items.length} items.`);

      response.items.forEach((company) => allCompanies.push(company));
      currentContinuationToken = response.continuationToken;
    } while (currentContinuationToken); // Continues as long as there's a token for the next page

    console.log(
      `\nSuccessfully fetched ${allCompanies.length} active companies.`
    );
  } catch (error) {
    console.error("Error fetching all active companies:", error);
  }
}

getAllActiveCompaniesManually();

Fetching Related Entities (include Clause)

Fetch primary entities and their related entities using the include option in AutotaskQueryOptions.

Structure of IncludeClause

interface IncludeClause {
  path: string; // Name of the relationship path (e.g., "Account" for a Ticket).
  // MUST correspond to a defined relationship for the primary entity.
  select?: string[]; // Optional: Fields to retrieve for the related entity.
}

Relationship definitions are internally managed by the SDK (see relationships/index.ts).

How it Works

  1. Client fetches primary entities.
  2. For each IncludeClause, it looks up the relationship configuration to find foreignEntity, localKey (on primary), and foreignKey (on related).
  3. Collects localKey values from primary entities.
  4. Makes additional API calls (using queryAllEntities internally) to fetch related entities, filtered by collected foreign key values.
  5. Embeds related entities into primary entity objects under a property matching path.
  6. If select is used, only specified fields are on the embedded entity.

Current Limitation: Supports one level of relationship.

Example: Tickets with Account and Contact

// Assume 'client' is an instantiated AutotaskClient
// Assume 'Tickets' entity has relationships 'Account' (to 'Companies') and 'Contact' (to 'Contacts')

async function getTicketsWithDetails() {
  try {
    const options: AutotaskQueryOptions = {
      filter: [{ field: "status", op: "eq", value: 1 }], // Example: Open tickets
      fields: ["id", "title", "companyID", "contactID"],
      include: [
        { path: "Account", select: ["id", "companyName"] },
        { path: "Contact", select: ["id", "firstName", "lastName"] },
      ],
      sdkPageSize: 5,
    };

    const response = await client.queryEntities("Tickets", options);

    response.items.forEach((ticket) => {
      // Type assertion for included properties
      const typedTicket = ticket as AutotaskEntityTypeMap["Tickets"] & {
        Account?: Partial<AutotaskEntityTypeMap["Companies"]>;
        Contact?: Partial<AutotaskEntityTypeMap["Contacts"]>;
      };
      console.log(`Ticket: ${typedTicket.title}`);
      if (typedTicket.Account)
        console.log(`  Account: ${typedTicket.Account.companyName}`);
      if (typedTicket.Contact)
        console.log(
          `  Contact: ${typedTicket.Contact.firstName} ${typedTicket.Contact.lastName}`
        );
    });

    if (response.errors)
      console.warn("\nErrors during fetch/embedding:", response.errors);
  } catch (error) {
    console.error("Error fetching tickets with details:", error);
  }
}
getTicketsWithDetails();

Fetching Entity Field Definitions and Picklists

The SDK provides a method to retrieve detailed information about the fields of any Autotask entity, including their data types and picklist options. This is particularly useful for dynamically building UIs, validating data, or understanding entity structures programmatically.

The getEntityFieldsDetailed method on the AutotaskClient instance allows you to fetch this information.

import {
  AutotaskClient,
  ProcessedFieldDetailedInfo, // Make sure this type is imported
  // ... other necessary imports
} from "@panoptic-it-solutions/autotask-client";

// Assume 'client' is an instantiated AutotaskClient

async function logTicketFieldDetails() {
  try {
    const entityName = "Tickets"; // Example: Get fields for the "Tickets" entity
    
    // Fetch all fields for the "Tickets" entity
    const allTicketFields: ProcessedFieldDetailedInfo[] = 
      await client.getEntityFieldsDetailed(entityName);

    console.log(`Field details for ${entityName}:`);
    allTicketFields.forEach(field => {
      console.log(`- Field Name: ${field.name}`);
      console.log(`  Data Type: ${field.dataType}`);
      console.log(`  Is Picklist: ${field.isPickList}`);
      if (field.isPickList && field.picklistMap) {
        console.log(`  Picklist Options (Value: Label):`);
        for (const [value, label] of Object.entries(field.picklistMap)) {
          console.log(`    '${value}': '${label}'`);
        }
      }
      // Example of accessing raw picklistValues if needed
      // if (field.isPickList && Array.isArray(field.picklistValues)) {
      //   console.log(`  Raw Picklist Values (first 2):`, field.picklistValues.slice(0, 2));
      // }
    });

    // Example: Fetch specific fields for the "Companies" entity
    const specificCompanyFields: ProcessedFieldDetailedInfo[] = 
      await client.getEntityFieldsDetailed("Companies", ["companyName", "companyType", "marketSegmentID"]);
    
    console.log(`\nSpecific field details for Companies:`);
    specificCompanyFields.forEach(field => {
      console.log(`- Field: ${field.name}, Type: ${field.dataType}${field.isPickList ? ', Picklist' : ''}`);
      if (field.name === "marketSegmentID" && field.picklistMap) {
        console.log(`  Market Segment Picklist:`, field.picklistMap);
      }
    });

  } catch (error) {
    console.error(`Error fetching field details:`, error);
  }
}

logTicketFieldDetails();

Understanding ProcessedFieldDetailedInfo

The getEntityFieldsDetailed method returns an array of ProcessedFieldDetailedInfo objects. Each object contains:

  • All properties from AutotaskRawField, which represents the raw data returned by the Autotask API for a field (e.g., name, dataType, isPickList, isReadOnly, isRequired, picklistValues array).
  • An optional picklistMap: If isPickList is true and picklistValues are available, this property provides a convenient key-value map where keys are the picklist item values (as strings) and values are their corresponding labels. This simplifies looking up display labels for picklist values.

For example, if a "Status" field (isPickList: true) has picklist values like { value: 1, label: "New" }, { value: 2, label: "In Progress" }, the picklistMap would be { "1": "New", "2": "In Progress" }.

Key Types and Interfaces

  • AllAutotaskEntityNames: Union type of all valid Autotask entity names (e.g., "Companies").
  • AutotaskEntityTypeMap: Maps entity names to their TypeScript interfaces (e.g., AutotaskEntityTypeMap["Tickets"] is TicketEntityType).
  • AutotaskQueryOptions: Options for queryEntities and queryAllEntities.
  • AutotaskApiFilter, AutotaskApiFilterOp: For filter conditions.
  • SdkClientPagedResponse<T>: Response from queryEntities.
  • SdkProgressData<T>: Progress object for queryAllEntities callback.
  • IncludeClause: For defining related entities to embed.
  • AutotaskClientConfig: Configuration for AutotaskClient.
  • AutotaskRawField: Interface representing the raw field definition as returned by Autotask's /entityInformation/fields endpoint.
  • ProcessedFieldDetailedInfo: Extends AutotaskRawField and includes an optional picklistMap for easier consumption of picklist options. This is the return type for client.getEntityFieldsDetailed().
  • AutotaskErrorItem: Structure of errors in API responses.
  • Custom Error Classes (AutotaskApiError, RateLimitError, NetworkError, TimeoutError, AutotaskClientError): Provide detailed error context.

These types are generally exported from @panoptic-it-solutions/autotask-client or accessible via its main exports. Refer to the SDK's types, autotask-types, and fields directories for detailed definitions.

Error Handling

  • Wrap SDK calls in try...catch blocks.
  • Check response.errors in SdkClientPagedResponse for non-fatal API errors.
  • Handle specific custom error types for better diagnostics.
try {
  const response = await client.queryEntities("Companies", {
    /* ... */
  });
  if (response.errors && response.errors.length > 0) {
    console.warn("API reported errors:", response.errors);
  }
  // Process response.items
} catch (error) {
  if (error instanceof AutotaskApiError) {
    console.error("Autotask API Error:", error.statusCode, error.errorsFromApi);
  } else if (error instanceof RateLimitError) {
    console.error("Rate limit hit (retries exhausted):", error.message);
  } else {
    console.error("SDK or Network Error:", error);
  }
}

Rate Limiting Details

  • Uses Upstash Redis (if configured) for distributed rate limiting. Essential for serverless/scaled environments.
  • Defaults (e.g., 5 req/sec, 5000 req/hr) are configurable in AutotaskClientConfig.upstash.
  • Automatic retries with backoff for RateLimitError or API 429s.
  • Without Upstash, falls back to reactive retries on 429s (less robust).

Source Code Structure Overview

  • src/client.ts: Main AutotaskClient class.
  • src/types/: Core type definitions (config, API responses, errors).
  • src/autotask-types/: Generated TypeScript interfaces for all Autotask entities.
  • src/relationships/: Defines entity relationships for the include feature.
  • src/client-internals/: Lower-level SDK mechanics (pagination, retry, request execution).
  • src/rate-limiting/: Rate limiting implementation.

Platform-Specific Configuration Examples

How you configure and use the AutotaskClient can vary. Using Upstash Redis for rate limiting is strongly recommended for all production and serverless/scaled environments.

1. Standard Node.js (e.g., Express.js Server)

Instantiate AutotaskClient once (singleton pattern) and reuse.

// src/services/autotaskService.ts
import {
  AutotaskClient,
  AutotaskClientConfig,
  LogLevel,
} from "@panoptic-it-solutions/autotask-client";
import * as dotenv from "dotenv";

dotenv.config();

const clientConfig: AutotaskClientConfig = {
  userName: process.env.AUTOTASK_USERNAME!,
  secret: process.env.AUTOTASK_SECRET!,
  integrationCode: process.env.AUTOTASK_API_INTEGRATION_CODE!,
  apiBaseUrl: process.env.AUTOTASK_API_URL!,
  upstash: {
    upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL,
    upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN,
  },
  logLevel: LogLevel.INFO,
};

const autotaskClient = new AutotaskClient(clientConfig);
export default autotaskClient;

// Usage in an Express route
// import autotaskClient from '../services/autotaskService';
// router.get('/companies', async (req, res) => { /* ... use autotaskClient ... */ });

2. Next.js / Vercel (Serverless Functions)

Instantiate AutotaskClient inside your API route handler. Upstash Redis is critical here.

// pages/api/autotask/contacts.ts
import type { NextApiRequest, NextApiResponse } from "next";
import {
  AutotaskClient,
  AutotaskClientConfig /* other types */,
} from "@panoptic-it-solutions/autotask-client";

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  if (req.method === "GET") {
    try {
      const clientConfig: AutotaskClientConfig = {
        userName: process.env.AUTOTASK_USERNAME!,
        secret: process.env.AUTOTASK_SECRET!,
        integrationCode: process.env.AUTOTASK_API_INTEGRATION_CODE!,
        apiBaseUrl: process.env.AUTOTASK_API_URL!,
        upstash: {
          // Essential for serverless
          upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL,
          upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN,
        },
      };
      const client = new AutotaskClient(clientConfig);
      // ... use client to fetch data ...
      // res.status(200).json(contactData);
    } catch (error: any) {
      console.error("Autotask API Error:", error);
      res.status(500).json({ error: error.message || "Failed to fetch" });
    }
  } // ...
}

3. Other Serverless Environments (e.g., AWS Lambda, Google Cloud Functions)

Principles are similar: instantiate client in handler, use distributed rate limiting (Upstash).

// Example for AWS Lambda
import {
  AutotaskClient,
  AutotaskClientConfig /* other types */,
} from "@panoptic-it-solutions/autotask-client";

export const handler = async (event: any) => {
  try {
    const clientConfig: AutotaskClientConfig = {
      userName: process.env.AUTOTASK_USERNAME!,
      // ... other config from environment variables ...
      upstash: {
        upstashRedisUrl: process.env.UPSTASH_REDIS_REST_URL,
        upstashRedisToken: process.env.UPSTASH_REDIS_REST_TOKEN,
      },
    };
    const client = new AutotaskClient(clientConfig);
    // ... use client ...
    // return { statusCode: 200, body: JSON.stringify(data) };
  } catch (error: any) {
    // ... error handling ...
  }
};

Rules for AI

We have created instructions and best practices for AI assistants when working with the @@panoptic-it-solutions/autotask-client SDK in a codebase. To view these rules open the gist here

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

License

This project is licensed under the AGPLv3 License. See the LICENSE file for details.

0.7.4

5 months ago

0.7.3

5 months ago

0.7.2

5 months ago

0.7.1

6 months ago

0.7.0

6 months ago

0.6.0

6 months ago

0.5.1

6 months ago

0.5.0

6 months ago

0.4.3

6 months ago

0.4.1

6 months ago

0.4.0

6 months ago

0.3.0

6 months ago

0.2.2

6 months ago

0.2.1

6 months ago

0.2.0

6 months ago

0.1.24

6 months ago

0.1.22

6 months ago

0.1.21

6 months ago

0.1.20

6 months ago

0.1.18

6 months ago

0.1.17

6 months ago

0.1.16

6 months ago

0.1.15

6 months ago

0.1.14

6 months ago

0.1.13

6 months ago

0.1.12

6 months ago

0.1.11

6 months ago

0.1.10

6 months ago

0.1.9

6 months ago

0.1.8

6 months ago

0.1.7

6 months ago

0.1.6

6 months ago

0.1.4

6 months ago

0.1.3

6 months ago

0.1.1

6 months ago

0.1.0

6 months ago