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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ const project = await client.projects.create("workspace-slug", {
- **Workspace**: Workspace-level operations
- **Epics**: Epic management and organization
- **Intake**: Intake form and request management
- **Stickies**: Stickies management
- **Teamspaces**: Teamspace management
- **Initiatives**: Initiative management
- **Features**: Workspace and project features management

## Development

Expand Down Expand Up @@ -129,6 +133,8 @@ pnpm test

# Run specific test files
pnpx ts-node tests/page.test.ts
# or
pnpm test page.test.ts
```

## License
Expand Down
4 changes: 4 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Plane Node SDK Test Configuration
# Copy this file to .env.test and update with your test environment values

# API configuration
PLANE_API_KEY=your-plane-api-key
PLANE_BASE_URL=your-plane-base-url

# Workspace configuration
TEST_WORKSPACE_SLUG=your-workspace-slug

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@makeplane/plane-node-sdk",
"version": "0.2.0",
"version": "0.2.1",
"description": "Node SDK for Plane",
"author": "Plane <engineering@plane.so>",
"repository": {
Expand Down
3 changes: 2 additions & 1 deletion src/api/BaseResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
/**
* Sanitize headers to remove sensitive information
*/
private sanitizeHeaders(headers: any): any {

Check warning on line 23 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type

Check warning on line 23 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
if (!headers) return headers;

const sanitized = { ...headers };
Expand All @@ -39,7 +39,7 @@
/**
* Sanitize data to remove sensitive information and limit size
*/
private sanitizeData(data: any): any {

Check warning on line 42 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type

Check warning on line 42 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
if (!data) return data;

// If data is too large, truncate it
Expand All @@ -54,7 +54,7 @@
/**
* GET request
*/
protected async get<T>(endpoint: string, params?: any): Promise<T> {

Check warning on line 57 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
try {
const response = await axios.get<T>(this.buildUrl(endpoint), {
headers: this.getHeaders(),
Expand All @@ -69,7 +69,7 @@
/**
* POST request
*/
protected async post<T>(endpoint: string, data?: any): Promise<T> {

Check warning on line 72 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
try {
const response = await axios.post<T>(this.buildUrl(endpoint), data, {
headers: this.getHeaders(),
Expand All @@ -83,7 +83,7 @@
/**
* PUT request
*/
protected async put<T>(endpoint: string, data?: any): Promise<T> {

Check warning on line 86 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
try {
const response = await axios.put<T>(this.buildUrl(endpoint), data, {
headers: this.getHeaders(),
Expand All @@ -97,7 +97,7 @@
/**
* PATCH request
*/
protected async patch<T>(endpoint: string, data?: any): Promise<T> {

Check warning on line 100 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
try {
const response = await axios.patch<T>(this.buildUrl(endpoint), data, {
headers: this.getHeaders(),
Expand All @@ -111,10 +111,11 @@
/**
* DELETE request
*/
protected async httpDelete(endpoint: string): Promise<void> {
protected async httpDelete(endpoint: string, data?: any): Promise<void> {

Check warning on line 114 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
try {
await axios.delete(this.buildUrl(endpoint), {
headers: this.getHeaders(),
data,
});
} catch (error) {
throw this.handleError(error);
Expand Down Expand Up @@ -152,7 +153,7 @@
/**
* Centralized error handling
*/
protected handleError(error: any): never {

Check warning on line 156 in src/api/BaseResource.ts

View workflow job for this annotation

GitHub Actions / build-lint

Unexpected any. Specify a different type
console.error("❌ [ERROR]", error);

if (error instanceof HttpError) {
Expand Down
37 changes: 37 additions & 0 deletions src/api/Initiatives/Epics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { Epic } from "../../models/Epic";
import { PaginatedResponse } from "../../models/common";
import { AddInitiativeEpicsRequest, RemoveInitiativeEpicsRequest } from "../../models/Initiative";

/**
* Initiative Epics API resource
* Handles initiative epic relationships
*/
export class Epics extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* Get epics associated with an initiative
*/
async list(workspaceSlug: string, initiativeId: string, params?: { limit?: number; offset?: number }): Promise<PaginatedResponse<Epic>> {
return this.get<PaginatedResponse<Epic>>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/epics/`, params);
}

/**
* Add epics to an initiative
*/
async add(workspaceSlug: string, initiativeId: string, addEpics: AddInitiativeEpicsRequest): Promise<Epic[]> {
return this.post<Epic[]>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/epics/`, addEpics);
}

/**
* Remove epics from an initiative
*/
async remove(workspaceSlug: string, initiativeId: string, removeEpics: RemoveInitiativeEpicsRequest): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/epics/`, removeEpics);
}
}

73 changes: 73 additions & 0 deletions src/api/Initiatives/Labels.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { InitiativeLabel, CreateInitiativeLabel, UpdateInitiativeLabel, ListInitiativeLabelsParams } from "../../models/InitiativeLabel";
import { PaginatedResponse } from "../../models/common";
import { AddInitiativeLabelsRequest, RemoveInitiativeLabelsRequest } from "../../models/Initiative";

/**
* Initiative Labels API resource
* Handles initiative label relationships
*/
export class Labels extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* Create a new initiative label
*/
async create(workspaceSlug: string, createInitiativeLabel: CreateInitiativeLabel): Promise<InitiativeLabel> {
return this.post<InitiativeLabel>(`/workspaces/${workspaceSlug}/initiatives/labels/`, createInitiativeLabel);
}

/**
* Retrieve an initiative label by ID
*/
async retrieve(workspaceSlug: string, initiativeLabelId: string): Promise<InitiativeLabel> {
return this.get<InitiativeLabel>(`/workspaces/${workspaceSlug}/initiatives/labels/${initiativeLabelId}/`);
}

/**
* Update an initiative label
*/
async update(workspaceSlug: string, initiativeLabelId: string, updateInitiativeLabel: UpdateInitiativeLabel): Promise<InitiativeLabel> {
return this.patch<InitiativeLabel>(`/workspaces/${workspaceSlug}/initiatives/labels/${initiativeLabelId}/`, updateInitiativeLabel);
}

/**
* Delete an initiative label
*/
async delete(workspaceSlug: string, initiativeLabelId: string): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/initiatives/labels/${initiativeLabelId}/`);
}

/**
* List initiative labels with optional filtering
*/
async list(workspaceSlug: string, params?: ListInitiativeLabelsParams): Promise<PaginatedResponse<InitiativeLabel>> {
return this.get<PaginatedResponse<InitiativeLabel>>(`/workspaces/${workspaceSlug}/initiatives/labels/`, params);
}

/**
* Add labels to an initiative
*/
async addLabels(workspaceSlug: string, initiativeId: string, addLabels: AddInitiativeLabelsRequest): Promise<InitiativeLabel[]> {
return this.post<InitiativeLabel[]>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/labels/`, addLabels);
}

/**
* Remove labels from an initiative
*/
async removeLabels(workspaceSlug: string, initiativeId: string, removeLabels: RemoveInitiativeLabelsRequest): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/labels/`, removeLabels);
}

/**
* Get labels associated with an initiative
*/
async listLabels(workspaceSlug: string, initiativeId: string, params?: { limit?: number; offset?: number }): Promise<PaginatedResponse<InitiativeLabel>> {
return this.get<PaginatedResponse<InitiativeLabel>>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/labels/`, params);
}

}

37 changes: 37 additions & 0 deletions src/api/Initiatives/Projects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { Project } from "../../models/Project";
import { PaginatedResponse } from "../../models/common";
import { AddInitiativeProjectsRequest, RemoveInitiativeProjectsRequest } from "../../models/Initiative";

/**
* Initiative Projects API resource
* Handles initiative project relationships
*/
export class Projects extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* Get projects associated with an initiative
*/
async list(workspaceSlug: string, initiativeId: string, params?: { limit?: number; offset?: number }): Promise<PaginatedResponse<Project>> {
return this.get<PaginatedResponse<Project>>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/projects/`, params);
}

/**
* Add projects to an initiative
*/
async add(workspaceSlug: string, initiativeId: string, addProjects: AddInitiativeProjectsRequest): Promise<Project[]> {
return this.post<Project[]>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/projects/`, addProjects);
}

/**
* Remove projects from an initiative
*/
async remove(workspaceSlug: string, initiativeId: string, removeProjects: RemoveInitiativeProjectsRequest): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/projects/`, removeProjects);
}
}

60 changes: 60 additions & 0 deletions src/api/Initiatives/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { Initiative, CreateInitiative, UpdateInitiative, ListInitiativesParams } from "../../models/Initiative";
import { PaginatedResponse } from "../../models/common";
import { Labels } from "./Labels";
import { Projects } from "./Projects";
import { Epics } from "./Epics";

/**
* Initiatives API resource
* Handles all initiative-related operations
*/
export class Initiatives extends BaseResource {
public labels: Labels;
public projects: Projects;
public epics: Epics;

constructor(config: Configuration) {
super(config);
this.labels = new Labels(config);
this.projects = new Projects(config);
this.epics = new Epics(config);
}

/**
* Create a new initiative
*/
async create(workspaceSlug: string, createInitiative: CreateInitiative): Promise<Initiative> {
return this.post<Initiative>(`/workspaces/${workspaceSlug}/initiatives/`, createInitiative);
}

/**
* Retrieve an initiative by ID
*/
async retrieve(workspaceSlug: string, initiativeId: string): Promise<Initiative> {
return this.get<Initiative>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/`);
}

/**
* Update an initiative
*/
async update(workspaceSlug: string, initiativeId: string, updateInitiative: UpdateInitiative): Promise<Initiative> {
return this.patch<Initiative>(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/`, updateInitiative);
}

/**
* Delete an initiative
*/
async delete(workspaceSlug: string, initiativeId: string): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/initiatives/${initiativeId}/`);
}

/**
* List initiatives with optional filtering
*/
async list(workspaceSlug: string, params?: ListInitiativesParams): Promise<PaginatedResponse<Initiative>> {
return this.get<PaginatedResponse<Initiative>>(`/workspaces/${workspaceSlug}/initiatives/`, params);
}
}

19 changes: 19 additions & 0 deletions src/api/Projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Configuration } from "../Configuration";
import { Project, CreateProject, UpdateProject, ListProjectsParams } from "../models/Project";
import { PaginatedResponse } from "../models/common";
import { User } from "../models/User";
import { ProjectFeatures, UpdateProjectFeatures } from "../models/ProjectFeatures";

/**
* Project API resource
Expand Down Expand Up @@ -65,4 +66,22 @@ export class Projects extends BaseResource {
async getTotalWorkLogs(workspaceSlug: string, projectId: string): Promise<any> {
return this.get<any>(`/workspaces/${workspaceSlug}/projects/${projectId}/work-logs/total/`);
}

/**
* Retrieve project features
*/
async retrieveFeatures(workspaceSlug: string, projectId: string): Promise<ProjectFeatures> {
return this.get<ProjectFeatures>(`/workspaces/${workspaceSlug}/projects/${projectId}/features/`);
}

/**
* Update project features
*/
async updateFeatures(
workspaceSlug: string,
projectId: string,
updateFeatures: UpdateProjectFeatures
): Promise<ProjectFeatures> {
return this.patch<ProjectFeatures>(`/workspaces/${workspaceSlug}/projects/${projectId}/features/`, updateFeatures);
}
}
49 changes: 49 additions & 0 deletions src/api/Stickies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { BaseResource } from "./BaseResource";
import { Configuration } from "../Configuration";
import { PaginatedResponse } from "../models/common";
import { Sticky, CreateSticky, UpdateSticky, ListStickiesParams } from "../models/Sticky";

/**
* Sticky API resource
* Handles all sticky related operations
*/
export class Stickies extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* Create a new sticky
*/
async create(workspaceSlug: string, createSticky: CreateSticky): Promise<Sticky> {
return this.post<Sticky>(`/workspaces/${workspaceSlug}/stickies/`, createSticky);
}

/**
* Retrieve a sticky by ID
*/
async retrieve(workspaceSlug: string, stickyId: string): Promise<Sticky> {
return this.get<Sticky>(`/workspaces/${workspaceSlug}/stickies/${stickyId}/`);
}

/**
* Update a sticky
*/
async update(workspaceSlug: string, stickyId: string, updateSticky: UpdateSticky): Promise<Sticky> {
return this.patch<Sticky>(`/workspaces/${workspaceSlug}/stickies/${stickyId}/`, updateSticky);
}

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

/**
* List stickies with optional filtering
*/
async list(workspaceSlug: string, params?: ListStickiesParams): Promise<PaginatedResponse<Sticky>> {
return this.get<PaginatedResponse<Sticky>>(`/workspaces/${workspaceSlug}/stickies/`, params);
}
}
37 changes: 37 additions & 0 deletions src/api/Teamspaces/Members.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { User } from "../../models/User";
import { PaginatedResponse } from "../../models/common";
import { AddTeamspaceMembersRequest, RemoveTeamspaceMembersRequest } from "../../models/Teamspace";

/**
* Teamspace Members API resource
* Handles teamspace member relationships
*/
export class Members extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* Get members associated with a teamspace
*/
async list(workspaceSlug: string, teamspaceId: string, params?: { limit?: number; offset?: number }): Promise<PaginatedResponse<User>> {
return this.get<PaginatedResponse<User>>(`/workspaces/${workspaceSlug}/teamspaces/${teamspaceId}/members/`, params);
}

/**
* Add members to a teamspace
*/
async add(workspaceSlug: string, teamspaceId: string, addMembers: AddTeamspaceMembersRequest): Promise<User[]> {
return this.post<User[]>(`/workspaces/${workspaceSlug}/teamspaces/${teamspaceId}/members/`, addMembers);
}

/**
* Remove members from a teamspace
*/
async remove(workspaceSlug: string, teamspaceId: string, removeMembers: RemoveTeamspaceMembersRequest): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/teamspaces/${teamspaceId}/members/`, removeMembers);
}
}

Loading