Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Sensitive information like API keys and authorization tokens are automatically r
When adding new examples:

1. Create a new `.ts` file in this directory
2. Import the SDK: `import { PlaneClient } from '../src'`
2. Import the SDK: `import { PlaneClient } from '../../src'`
3. Include proper error handling
4. Add documentation comments
5. Update this README with a description
Expand Down
15 changes: 3 additions & 12 deletions examples/bootstrap-project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// examples/project-bootstrap.ts
import { CreateWorkItemProperty, PlaneClient, WorkItemProperty } from "../src";
import { CreateWorkItemProperty, PlaneClient, WorkItemProperty } from "../../src";

async function bootstrapProject(workspaceSlug: string) {
const client = new PlaneClient({
Expand Down Expand Up @@ -30,11 +30,7 @@ async function bootstrapProject(workspaceSlug: string) {
];

for (const type of workItemTypes) {
const workItemType = await client.workItemTypes.create(
workspaceSlug,
project.id,
type
);
const workItemType = await client.workItemTypes.create(workspaceSlug, project.id, type);
const workItemTypeId = workItemType.id;
const customProperties: CreateWorkItemProperty[] = [
{
Expand All @@ -47,12 +43,7 @@ async function bootstrapProject(workspaceSlug: string) {
];
// 3. Create custom properties
for (const prop of customProperties) {
await client.workItemProperties.create(
workspaceSlug,
project.id,
workItemTypeId,
prop
);
await client.workItemProperties.create(workspaceSlug, project.id, workItemTypeId, prop);
}
}

Expand Down
25 changes: 25 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/tests'],
testMatch: [
'**/__tests__/**/*.ts',
'**/?(*.)+(spec|test).ts'
],
transform: {
'^.+\\.ts$': ['ts-jest', {
tsconfig: 'tsconfig.jest.json'
}],
},
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/*.spec.ts'
],
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
testTimeout: 30000, // 30 seconds timeout for API tests
verbose: true,
// Allow tests to run in parallel but with some control
maxWorkers: 1, // Run tests sequentially to avoid API rate limits
};
7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"test": "ts-node tests/run-all.test.ts",
"test": "jest",
"test:unit": "jest --testPathPattern=tests/unit",
"test:e2e": "jest --testPathPattern=tests/e2e",
"test:all": "pnpm test:unit && pnpm test:e2e",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"lint": "eslint src/**/*.ts",
"format": "prettier --write src/**/*.ts",
"clean": "rm -rf dist"
Expand Down
2 changes: 2 additions & 0 deletions src/api/BaseResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ export abstract class BaseResource {
* Centralized error handling
*/
protected handleError(error: any): never {
console.error("❌ [ERROR]", error);

if (error instanceof HttpError) {
throw error;
}
Expand Down
42 changes: 12 additions & 30 deletions src/api/Customers/Properties.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
ListCustomerPropertiesParams,
CreateCustomerPropertyRequest,
UpdateCustomerPropertyRequest,
CustomPropertyValueResponse,
} from "../../models/Customer";
import { PaginatedResponse } from "../../models/common";

/**
* Customer Properties API resource
Expand All @@ -27,11 +29,8 @@ export class Properties extends BaseResource {
async listPropertyDefinitions(
workspaceSlug: string,
params?: ListCustomerPropertiesParams
): Promise<CustomerProperty[]> {
return this.get<CustomerProperty[]>(
`/workspaces/${workspaceSlug}/customer-properties/`,
params
);
): Promise<PaginatedResponse<CustomerProperty>> {
return this.get<PaginatedResponse<CustomerProperty>>(`/workspaces/${workspaceSlug}/customer-properties/`, params);
}

/**
Expand All @@ -41,22 +40,14 @@ export class Properties extends BaseResource {
workspaceSlug: string,
createProperty: CreateCustomerPropertyRequest
): Promise<CustomerProperty> {
return this.post<CustomerProperty>(
`/workspaces/${workspaceSlug}/customer-properties/`,
createProperty
);
return this.post<CustomerProperty>(`/workspaces/${workspaceSlug}/customer-properties/`, createProperty);
}

/**
* Retrieve customer property
*/
async retrievePropertyDefinition(
workspaceSlug: string,
propertyId: string
): Promise<CustomerProperty> {
return this.get<CustomerProperty>(
`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`
);
async retrievePropertyDefinition(workspaceSlug: string, propertyId: string): Promise<CustomerProperty> {
return this.get<CustomerProperty>(`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`);
}

/**
Expand All @@ -76,13 +67,8 @@ export class Properties extends BaseResource {
/**
* Delete customer property
*/
async deletePropertyDefinition(
workspaceSlug: string,
propertyId: string
): Promise<void> {
return this.httpDelete(
`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`
);
async deletePropertyDefinition(workspaceSlug: string, propertyId: string): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/customer-properties/${propertyId}/`);
}

// ===== CUSTOMER PROPERTY VALUES API METHODS =====
Expand All @@ -94,8 +80,8 @@ export class Properties extends BaseResource {
workspaceSlug: string,
customerId: string,
params?: ListCustomerPropertyValuesParams
): Promise<CustomerPropertyValue[]> {
return this.get<CustomerPropertyValue[]>(
): Promise<CustomPropertyValueResponse> {
return this.get<CustomPropertyValueResponse>(
`/workspaces/${workspaceSlug}/customers/${customerId}/property-values/`,
params
);
Expand All @@ -104,11 +90,7 @@ export class Properties extends BaseResource {
/**
* Get single property value
*/
async retrieveValue(
workspaceSlug: string,
customerId: string,
propertyId: string
): Promise<CustomerPropertyValue> {
async retrieveValue(workspaceSlug: string, customerId: string, propertyId: string): Promise<CustomerPropertyValue> {
return this.get<CustomerPropertyValue>(
`/workspaces/${workspaceSlug}/customers/${customerId}/property-values/${propertyId}/`
);
Expand Down
30 changes: 8 additions & 22 deletions src/api/Customers/Requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
UpdateCustomerRequest,
ListCustomerRequestsParams,
} from "../../models/Customer";
import { PaginatedResponse } from "../../models/common";

/**
* Customer Requests API resource
Expand All @@ -23,8 +24,8 @@ export class Requests extends BaseResource {
workspaceSlug: string,
customerId: string,
params?: ListCustomerRequestsParams
): Promise<CustomerRequest[]> {
return this.get<CustomerRequest[]>(
): Promise<PaginatedResponse<CustomerRequest>> {
return this.get<PaginatedResponse<CustomerRequest>>(
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/`,
params
);
Expand All @@ -38,23 +39,14 @@ export class Requests extends BaseResource {
customerId: string,
createRequest: CreateCustomerRequest
): Promise<CustomerRequest> {
return this.post<CustomerRequest>(
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/`,
createRequest
);
return this.post<CustomerRequest>(`/workspaces/${workspaceSlug}/customers/${customerId}/requests/`, createRequest);
}

/**
* Retrieve customer request
*/
async retrieve(
workspaceSlug: string,
customerId: string,
requestId: string
): Promise<CustomerRequest> {
return this.get<CustomerRequest>(
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`
);
async retrieve(workspaceSlug: string, customerId: string, requestId: string): Promise<CustomerRequest> {
return this.get<CustomerRequest>(`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`);
}

/**
Expand All @@ -75,13 +67,7 @@ export class Requests extends BaseResource {
/**
* Delete customer request
*/
async delete(
workspaceSlug: string,
customerId: string,
requestId: string
): Promise<void> {
return this.httpDelete(
`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`
);
async delete(workspaceSlug: string, customerId: string, requestId: string): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/customers/${customerId}/requests/${requestId}/`);
}
}
69 changes: 18 additions & 51 deletions src/api/Customers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
CreateCustomer,
UpdateCustomer,
ListCustomersParams,
LinkIssuesToCustomerResponse,
} from "../../models/Customer";
import { Properties } from "./Properties";
import { Requests } from "./Requests";
import { PaginatedResponse } from "../../models/common";

/**
* Customers API resource
Expand All @@ -27,73 +29,45 @@ export class Customers extends BaseResource {
/**
* Create a new customer
*/
async create(
workspaceSlug: string,
createCustomer: CreateCustomer
): Promise<Customer> {
return this.post<Customer>(
`/workspaces/${workspaceSlug}/customers/`,
createCustomer
);
async create(workspaceSlug: string, createCustomer: CreateCustomer): Promise<Customer> {
return this.post<Customer>(`/workspaces/${workspaceSlug}/customers/`, createCustomer);
}

/**
* Retrieve a customer by ID
*/
async retrieve(workspaceSlug: string, customerId: string): Promise<Customer> {
return this.get<Customer>(
`/workspaces/${workspaceSlug}/customers/${customerId}/`
);
return this.get<Customer>(`/workspaces/${workspaceSlug}/customers/${customerId}/`);
}

/**
* Update a customer
*/
async update(
workspaceSlug: string,
customerId: string,
updateCustomer: UpdateCustomer
): Promise<Customer> {
return this.patch<Customer>(
`/workspaces/${workspaceSlug}/customers/${customerId}/`,
updateCustomer
);
async update(workspaceSlug: string, customerId: string, updateCustomer: UpdateCustomer): Promise<Customer> {
return this.patch<Customer>(`/workspaces/${workspaceSlug}/customers/${customerId}/`, updateCustomer);
}

/**
* Delete a customer
*/
async delete(workspaceSlug: string, customerId: string): Promise<void> {
return this.httpDelete(
`/workspaces/${workspaceSlug}/customers/${customerId}/`
);
return this.httpDelete(`/workspaces/${workspaceSlug}/customers/${customerId}/`);
}

/**
* List customers with optional filtering
*/
async list(
workspaceSlug: string,
params?: ListCustomersParams
): Promise<Customer[]> {
return this.get<Customer[]>(
`/workspaces/${workspaceSlug}/customers/`,
params
);
async list(workspaceSlug: string, params?: ListCustomersParams): Promise<PaginatedResponse<Customer>> {
return this.get<PaginatedResponse<Customer>>(`/workspaces/${workspaceSlug}/customers/`, params);
}

// ===== CUSTOMER ISSUES API METHODS =====

/**
* List customer issues
*/
async listCustomerIssues(
workspaceSlug: string,
customerId: string
): Promise<any[]> {
return this.get<any[]>(
`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`
);
async listCustomerIssues(workspaceSlug: string, customerId: string): Promise<any[]> {
return this.get<any[]>(`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`);
}

/**
Expand All @@ -103,23 +77,16 @@ export class Customers extends BaseResource {
workspaceSlug: string,
customerId: string,
issueIds: string[]
): Promise<any> {
return this.post<any>(
`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`,
{ issue_ids: issueIds }
);
): Promise<LinkIssuesToCustomerResponse> {
return this.post<LinkIssuesToCustomerResponse>(`/workspaces/${workspaceSlug}/customers/${customerId}/issues/`, {
issue_ids: issueIds,
});
}

/**
* Unlink issue from customer
*/
async unlinkIssueFromCustomer(
workspaceSlug: string,
customerId: string,
issueId: string
): Promise<void> {
return this.httpDelete(
`/workspaces/${workspaceSlug}/customers/${customerId}/issues/${issueId}/`
);
async unlinkIssueFromCustomer(workspaceSlug: string, customerId: string, issueId: string): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/customers/${customerId}/issues/${issueId}/`);
}
}
13 changes: 11 additions & 2 deletions src/api/Links.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BaseResource } from "./BaseResource";
import { Configuration } from "../Configuration";
import { Link, CreateLink, UpdateLink, ListLinksParams } from "../models/Link";
import { PaginatedResponse } from "../models/common";

/**
* Links API resource
Expand Down Expand Up @@ -54,7 +55,15 @@ export class Links extends BaseResource {
/**
* List links for a work item with optional filtering
*/
async list(workspaceSlug: string, projectId: string, issueId: string, params?: ListLinksParams): Promise<Link[]> {
return this.get<Link[]>(`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${issueId}/links/`, params);
async list(
workspaceSlug: string,
projectId: string,
issueId: string,
params?: ListLinksParams
): Promise<PaginatedResponse<Link>> {
return this.get<PaginatedResponse<Link>>(
`/workspaces/${workspaceSlug}/projects/${projectId}/work-items/${issueId}/links/`,
params
);
}
}
Loading