Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ const project = await client.projects.create("workspace-slug", {
- **Intake**: Intake form and request management
- **Stickies**: Stickies management
- **Teamspaces**: Teamspace management
- **Milestones**: Milestone tracking and management
- **Initiatives**: Initiative management
- **AgentRuns**: AI agent run orchestration and activity tracking
- **Workflows**: Project workflow management with state attachments and transitions
- **ProjectTemplates**: Work item and page template management per project
- **Features**: Workspace and project features management

## Development
Expand Down
52 changes: 52 additions & 0 deletions src/api/ProjectTemplates/Pages.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { CreatePageTemplate, PageTemplate, UpdatePageTemplate } from "../../models/ProjectTemplate";

/**
* ProjectPageTemplates sub-resource
* Manages page templates within a project
*/
export class Pages extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* List all page templates for a project
*/
async list(workspaceSlug: string, projectId: string): Promise<PageTemplate[]> {
const data = await this.get<PageTemplate[] | { results: PageTemplate[] }>(
`/workspaces/${workspaceSlug}/projects/${projectId}/pages/templates/`
);
return Array.isArray(data) ? data : data.results;
}

/**
* Create a new page template for a project
*/
async create(workspaceSlug: string, projectId: string, data: CreatePageTemplate): Promise<PageTemplate> {
return this.post<PageTemplate>(`/workspaces/${workspaceSlug}/projects/${projectId}/pages/templates/`, data);
}

/**
* Update a page template by ID
*/
async update(
workspaceSlug: string,
projectId: string,
templateId: string,
data: UpdatePageTemplate
): Promise<PageTemplate> {
return this.patch<PageTemplate>(
`/workspaces/${workspaceSlug}/projects/${projectId}/pages/templates/${templateId}/`,
data
);
}

/**
* Delete a page template by ID
*/
async del(workspaceSlug: string, projectId: string, templateId: string): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/projects/${projectId}/pages/templates/${templateId}/`);
}
}
52 changes: 52 additions & 0 deletions src/api/ProjectTemplates/WorkItems.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { CreateWorkItemTemplate, UpdateWorkItemTemplate, WorkItemTemplate } from "../../models/ProjectTemplate";

/**
* ProjectWorkItemTemplates sub-resource
* Manages work item templates within a project
*/
export class WorkItems extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* List all work item templates for a project
*/
async list(workspaceSlug: string, projectId: string): Promise<WorkItemTemplate[]> {
const data = await this.get<WorkItemTemplate[] | { results: WorkItemTemplate[] }>(
`/workspaces/${workspaceSlug}/projects/${projectId}/workitems/templates/`
);
return Array.isArray(data) ? data : data.results;
}

/**
* Create a new work item template for a project
*/
async create(workspaceSlug: string, projectId: string, data: CreateWorkItemTemplate): Promise<WorkItemTemplate> {
return this.post<WorkItemTemplate>(`/workspaces/${workspaceSlug}/projects/${projectId}/workitems/templates/`, data);
}

/**
* Update a work item template by ID
*/
async update(
workspaceSlug: string,
projectId: string,
templateId: string,
data: UpdateWorkItemTemplate
): Promise<WorkItemTemplate> {
return this.patch<WorkItemTemplate>(
`/workspaces/${workspaceSlug}/projects/${projectId}/workitems/templates/${templateId}/`,
data
);
}

/**
* Delete a work item template by ID
*/
async del(workspaceSlug: string, projectId: string, templateId: string): Promise<void> {
return this.httpDelete(`/workspaces/${workspaceSlug}/projects/${projectId}/workitems/templates/${templateId}/`);
}
}
19 changes: 19 additions & 0 deletions src/api/ProjectTemplates/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { WorkItems } from "./WorkItems";
import { Pages } from "./Pages";

/**
* ProjectTemplates API resource
* Container for work item and page template sub-resources
*/
export class ProjectTemplates extends BaseResource {
public workItems: WorkItems;
public pages: Pages;

constructor(config: Configuration) {
super(config);
this.workItems = new WorkItems(config);
this.pages = new Pages(config);
}
}
34 changes: 34 additions & 0 deletions src/api/Workflows/States.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { AttachWorkflowStates } from "../../models/Workflow";

/**
* WorkflowStates sub-resource
* Manages state attachments on a workflow
*/
export class States extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* Attach states to a workflow
*/
async attach(
workspaceSlug: string,
projectId: string,
workflowId: string,
data: AttachWorkflowStates
): Promise<void> {
return this.post<void>(`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/${workflowId}/states/`, data);
}

/**
* Detach a state from a workflow
*/
async detach(workspaceSlug: string, projectId: string, workflowId: string, stateId: string): Promise<void> {
return this.httpDelete(
`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/${workflowId}/states/${stateId}/`
);
}
}
75 changes: 75 additions & 0 deletions src/api/Workflows/Transitions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { HttpError } from "../../errors";
import { CreateWorkflowTransition, UpdateWorkflowTransition, WorkflowTransition } from "../../models/Workflow";

/**
* WorkflowTransitions sub-resource
* Manages state transitions within a workflow
*/
export class Transitions extends BaseResource {
constructor(config: Configuration) {
super(config);
}

/**
* List all state transitions for a workflow
*/
async list(workspaceSlug: string, projectId: string, workflowId: string): Promise<WorkflowTransition[]> {
const data = await this.get<WorkflowTransition[] | { results: WorkflowTransition[] }>(
`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/${workflowId}/state-transitions/`
);
return Array.isArray(data) ? data : data.results;
}

/**
* Create a state transition for a workflow.
* Returns null if the transition already exists (HTTP 400 "already exists").
*/
async create(
workspaceSlug: string,
projectId: string,
workflowId: string,
data: CreateWorkflowTransition
): Promise<WorkflowTransition | null> {
try {
return await this.post<WorkflowTransition>(
`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/${workflowId}/state-transitions/`,
data
);
} catch (error) {
if (error instanceof HttpError && error.statusCode === 400) {
const body = JSON.stringify(error.response ?? "").toLowerCase();
if (body.includes("already exists")) {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
return null;
}
}
throw error;
}
}

/**
* Update a workflow state transition
*/
async update(
workspaceSlug: string,
projectId: string,
workflowId: string,
transitionId: string,
data: UpdateWorkflowTransition
): Promise<WorkflowTransition> {
return this.patch<WorkflowTransition>(
`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/${workflowId}/state-transitions/${transitionId}/`,
data
);
}

/**
* Delete a workflow state transition
*/
async del(workspaceSlug: string, projectId: string, workflowId: string, transitionId: string): Promise<void> {
return this.httpDelete(
`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/${workflowId}/state-transitions/${transitionId}/`
);
}
}
44 changes: 44 additions & 0 deletions src/api/Workflows/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { BaseResource } from "../BaseResource";
import { Configuration } from "../../Configuration";
import { CreateWorkflow, UpdateWorkflow, Workflow } from "../../models/Workflow";
import { States } from "./States";
import { Transitions } from "./Transitions";

/**
* Workflows API resource
* Handles project workflow operations and exposes states/transitions sub-resources
*/
export class Workflows extends BaseResource {
public states: States;
public transitions: Transitions;

constructor(config: Configuration) {
super(config);
this.states = new States(config);
this.transitions = new Transitions(config);
}

/**
* List all workflows for a project
*/
async list(workspaceSlug: string, projectId: string): Promise<Workflow[]> {
const data = await this.get<Workflow[] | { results: Workflow[] }>(
`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/`
);
return Array.isArray(data) ? data : data.results;
}

/**
* Create a new workflow for a project
*/
async create(workspaceSlug: string, projectId: string, data: CreateWorkflow): Promise<Workflow> {
return this.post<Workflow>(`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/`, data);
}

/**
* Update a workflow by ID
*/
async update(workspaceSlug: string, projectId: string, workflowId: string, data: UpdateWorkflow): Promise<Workflow> {
return this.patch<Workflow>(`/workspaces/${workspaceSlug}/projects/${projectId}/workflows/${workflowId}/`, data);
}
}
6 changes: 6 additions & 0 deletions src/client/plane-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import { Teamspaces } from "../api/Teamspaces";
import { Initiatives } from "../api/Initiatives";
import { Milestones } from "../api/Milestones";
import { AgentRuns } from "../api/AgentRuns";
import { Workflows } from "../api/Workflows";
import { ProjectTemplates } from "../api/ProjectTemplates";

/**
* Main Plane Client class
Expand Down Expand Up @@ -48,6 +50,8 @@ export class PlaneClient {
public milestones: Milestones;
public initiatives: Initiatives;
public agentRuns: AgentRuns;
public workflows: Workflows;
public projectTemplates: ProjectTemplates;

constructor(config: { baseUrl?: string; apiKey?: string; accessToken?: string; enableLogging?: boolean }) {
this.config = new Configuration({
Expand Down Expand Up @@ -82,5 +86,7 @@ export class PlaneClient {
this.milestones = new Milestones(this.config);
this.initiatives = new Initiatives(this.config);
this.agentRuns = new AgentRuns(this.config);
this.workflows = new Workflows(this.config);
this.projectTemplates = new ProjectTemplates(this.config);
}
}
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ export { Teamspaces } from "./api/Teamspaces";
export { Milestones } from "./api/Milestones";
export { Initiatives } from "./api/Initiatives";
export { AgentRuns } from "./api/AgentRuns";
export { Workflows } from "./api/Workflows";
export { ProjectTemplates } from "./api/ProjectTemplates";

// Sub-resources
export { Relations as WorkItemRelations } from "./api/WorkItems/Relations";
Expand All @@ -49,6 +51,10 @@ export { Labels as InitiativeLabels } from "./api/Initiatives/Labels";
export { Projects as InitiativeProjects } from "./api/Initiatives/Projects";
export { Epics as InitiativeEpics } from "./api/Initiatives/Epics";
export { Activities as AgentRunActivities } from "./api/AgentRuns/Activities";
export { States as WorkflowStates } from "./api/Workflows/States";
export { Transitions as WorkflowTransitions } from "./api/Workflows/Transitions";
export { WorkItems as ProjectWorkItemTemplates } from "./api/ProjectTemplates/WorkItems";
export { Pages as ProjectPageTemplates } from "./api/ProjectTemplates/Pages";

// Models
export * from "./models";
Expand Down
46 changes: 46 additions & 0 deletions src/models/ProjectTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { BaseModel } from "./common";

/**
* Project template model interfaces
*/
export interface WorkItemTemplate extends BaseModel {
name: string;
short_description?: string;
template_data?: Record<string, unknown>;
template_type?: string;
project: string;
workspace: string;
}

export interface CreateWorkItemTemplate {
name: string;
short_description?: string;
template_data?: Record<string, unknown>;
}

export interface UpdateWorkItemTemplate {
name?: string;
short_description?: string;
template_data?: Record<string, unknown>;
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

export interface PageTemplate extends BaseModel {
name: string;
short_description?: string;
template_data?: Record<string, unknown>;
template_type?: string;
project: string;
workspace: string;
}

export interface CreatePageTemplate {
name: string;
short_description?: string;
template_data?: Record<string, unknown>;
}

export interface UpdatePageTemplate {
name?: string;
short_description?: string;
template_data?: Record<string, unknown>;
}
Loading
Loading