diff --git a/README.md b/README.md index 3fe460d..bd15891 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,13 @@ const project = await client.projects.create("workspace-slug", { - **Stickies**: Stickies management - **Teamspaces**: Teamspace management - **Initiatives**: Initiative management +- **WorkspaceTemplates**: Workspace-level work item, project, and page templates +- **WorkspaceWorkItemTypes**: Workspace-level work item type management with property links +- **WorkspaceWorkItemProperties**: Workspace-level custom property management with options +- **WorkspaceProjectLabels**: Workspace-level project label management +- **WorkspaceProjectStates**: Workspace-level project state management +- **WorkItemRelationDefinitions**: Custom work item relation type definitions +- **Releases**: Release management with tags, labels, and item label assignment - **Features**: Workspace and project features management ## Development diff --git a/src/api/Releases/ItemLabels.ts b/src/api/Releases/ItemLabels.ts new file mode 100644 index 0000000..4e5524b --- /dev/null +++ b/src/api/Releases/ItemLabels.ts @@ -0,0 +1,32 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { AddReleaseItemLabel, ReleaseLabel } from "../../models/Release"; + +/** + * ReleaseItemLabels sub-resource + * Manages labels assigned to a specific release + */ +export class ItemLabels extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string, releaseId: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/releases/${releaseId}/labels/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, releaseId: string, data: AddReleaseItemLabel): Promise { + const result = await this.post( + `/workspaces/${workspaceSlug}/releases/${releaseId}/labels/`, + data + ); + return Array.isArray(result) ? result : result.results; + } + + async del(workspaceSlug: string, releaseId: string, labelId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/releases/${releaseId}/labels/${labelId}/`); + } +} diff --git a/src/api/Releases/Labels.ts b/src/api/Releases/Labels.ts new file mode 100644 index 0000000..4d0ffdf --- /dev/null +++ b/src/api/Releases/Labels.ts @@ -0,0 +1,24 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { CreateReleaseLabel, ReleaseLabel } from "../../models/Release"; + +/** + * ReleaseLabels sub-resource + * Manages release labels at the workspace level + */ +export class Labels extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/releases/labels/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateReleaseLabel): Promise { + return this.post(`/workspaces/${workspaceSlug}/releases/labels/`, data); + } +} diff --git a/src/api/Releases/Tags.ts b/src/api/Releases/Tags.ts new file mode 100644 index 0000000..fc05b12 --- /dev/null +++ b/src/api/Releases/Tags.ts @@ -0,0 +1,24 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { CreateReleaseTag, ReleaseTag } from "../../models/Release"; + +/** + * ReleaseTags sub-resource + * Manages release tags at the workspace level + */ +export class Tags extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/releases/tags/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateReleaseTag): Promise { + return this.post(`/workspaces/${workspaceSlug}/releases/tags/`, data); + } +} diff --git a/src/api/Releases/index.ts b/src/api/Releases/index.ts new file mode 100644 index 0000000..485c227 --- /dev/null +++ b/src/api/Releases/index.ts @@ -0,0 +1,36 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { CreateRelease, Release, UpdateRelease } from "../../models/Release"; +import { Tags } from "./Tags"; +import { Labels } from "./Labels"; +import { ItemLabels } from "./ItemLabels"; + +/** + * Releases API resource + * Manages releases at the workspace level with tags, labels, and item-label sub-resources + */ +export class Releases extends BaseResource { + public tags: Tags; + public labels: Labels; + public itemLabels: ItemLabels; + + constructor(config: Configuration) { + super(config); + this.tags = new Tags(config); + this.labels = new Labels(config); + this.itemLabels = new ItemLabels(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get(`/workspaces/${workspaceSlug}/releases/`); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateRelease): Promise { + return this.post(`/workspaces/${workspaceSlug}/releases/`, data); + } + + async update(workspaceSlug: string, releaseId: string, data: UpdateRelease): Promise { + return this.patch(`/workspaces/${workspaceSlug}/releases/${releaseId}/`, data); + } +} diff --git a/src/api/WorkItemRelationDefinitions.ts b/src/api/WorkItemRelationDefinitions.ts new file mode 100644 index 0000000..9a3b0fc --- /dev/null +++ b/src/api/WorkItemRelationDefinitions.ts @@ -0,0 +1,52 @@ +import { BaseResource } from "./BaseResource"; +import { Configuration } from "../Configuration"; +import { + CreateWorkItemRelationDefinition, + ListWorkItemRelationDefinitionsParams, + UpdateWorkItemRelationDefinition, + WorkItemRelationDefinition, +} from "../models/WorkItemRelationDefinition"; + +/** + * WorkItemRelationDefinitions API resource + * Manages work item relation definitions at the workspace level + */ +export class WorkItemRelationDefinitions extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list( + workspaceSlug: string, + params?: ListWorkItemRelationDefinitionsParams + ): Promise { + const query: Record = {}; + if (params?.is_default !== undefined) query["is_default"] = String(params.is_default); + if (params?.is_active !== undefined) query["is_active"] = String(params.is_active); + + const data = await this.get( + `/workspaces/${workspaceSlug}/work-item-relation-definitions/`, + Object.keys(query).length ? query : undefined + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkItemRelationDefinition): Promise { + return this.post(`/workspaces/${workspaceSlug}/work-item-relation-definitions/`, data); + } + + async update( + workspaceSlug: string, + definitionId: string, + data: UpdateWorkItemRelationDefinition + ): Promise { + return this.patch( + `/workspaces/${workspaceSlug}/work-item-relation-definitions/${definitionId}/`, + data + ); + } + + async del(workspaceSlug: string, definitionId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/work-item-relation-definitions/${definitionId}/`); + } +} diff --git a/src/api/WorkspaceProjectLabels.ts b/src/api/WorkspaceProjectLabels.ts new file mode 100644 index 0000000..7c46bb9 --- /dev/null +++ b/src/api/WorkspaceProjectLabels.ts @@ -0,0 +1,40 @@ +import { BaseResource } from "./BaseResource"; +import { Configuration } from "../Configuration"; +import { + CreateWorkspaceProjectLabel, + UpdateWorkspaceProjectLabel, + WorkspaceProjectLabel, +} from "../models/WorkspaceProjectLabel"; + +/** + * WorkspaceProjectLabels API resource + * Manages project labels at the workspace level + */ +export class WorkspaceProjectLabels extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/project-labels/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkspaceProjectLabel): Promise { + return this.post(`/workspaces/${workspaceSlug}/project-labels/`, data); + } + + async update( + workspaceSlug: string, + labelId: string, + data: UpdateWorkspaceProjectLabel + ): Promise { + return this.patch(`/workspaces/${workspaceSlug}/project-labels/${labelId}/`, data); + } + + async del(workspaceSlug: string, labelId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/project-labels/${labelId}/`); + } +} diff --git a/src/api/WorkspaceProjectStates.ts b/src/api/WorkspaceProjectStates.ts new file mode 100644 index 0000000..89fd90f --- /dev/null +++ b/src/api/WorkspaceProjectStates.ts @@ -0,0 +1,40 @@ +import { BaseResource } from "./BaseResource"; +import { Configuration } from "../Configuration"; +import { + CreateWorkspaceProjectState, + UpdateWorkspaceProjectState, + WorkspaceProjectState, +} from "../models/WorkspaceProjectState"; + +/** + * WorkspaceProjectStates API resource + * Manages project states at the workspace level + */ +export class WorkspaceProjectStates extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/project-states/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkspaceProjectState): Promise { + return this.post(`/workspaces/${workspaceSlug}/project-states/`, data); + } + + async update( + workspaceSlug: string, + stateId: string, + data: UpdateWorkspaceProjectState + ): Promise { + return this.patch(`/workspaces/${workspaceSlug}/project-states/${stateId}/`, data); + } + + async del(workspaceSlug: string, stateId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/project-states/${stateId}/`); + } +} diff --git a/src/api/WorkspaceTemplates/Pages.ts b/src/api/WorkspaceTemplates/Pages.ts new file mode 100644 index 0000000..d16f13a --- /dev/null +++ b/src/api/WorkspaceTemplates/Pages.ts @@ -0,0 +1,40 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { + CreateWorkspacePageTemplate, + UpdateWorkspacePageTemplate, + WorkspacePageTemplate, +} from "../../models/WorkspaceTemplate"; + +/** + * WorkspacePageTemplates sub-resource + * Manages page templates at the workspace level + */ +export class Pages extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/pages/templates/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkspacePageTemplate): Promise { + return this.post(`/workspaces/${workspaceSlug}/pages/templates/`, data); + } + + async update( + workspaceSlug: string, + templateId: string, + data: UpdateWorkspacePageTemplate + ): Promise { + return this.patch(`/workspaces/${workspaceSlug}/pages/templates/${templateId}/`, data); + } + + async del(workspaceSlug: string, templateId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/pages/templates/${templateId}/`); + } +} diff --git a/src/api/WorkspaceTemplates/Projects.ts b/src/api/WorkspaceTemplates/Projects.ts new file mode 100644 index 0000000..85b1c51 --- /dev/null +++ b/src/api/WorkspaceTemplates/Projects.ts @@ -0,0 +1,40 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { + CreateWorkspaceProjectTemplate, + UpdateWorkspaceProjectTemplate, + WorkspaceProjectTemplate, +} from "../../models/WorkspaceTemplate"; + +/** + * WorkspaceProjectTemplates sub-resource + * Manages project templates at the workspace level + */ +export class Projects extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/projects/templates/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkspaceProjectTemplate): Promise { + return this.post(`/workspaces/${workspaceSlug}/projects/templates/`, data); + } + + async update( + workspaceSlug: string, + templateId: string, + data: UpdateWorkspaceProjectTemplate + ): Promise { + return this.patch(`/workspaces/${workspaceSlug}/projects/templates/${templateId}/`, data); + } + + async del(workspaceSlug: string, templateId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/projects/templates/${templateId}/`); + } +} diff --git a/src/api/WorkspaceTemplates/WorkItems.ts b/src/api/WorkspaceTemplates/WorkItems.ts new file mode 100644 index 0000000..5460b44 --- /dev/null +++ b/src/api/WorkspaceTemplates/WorkItems.ts @@ -0,0 +1,43 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { + CreateWorkspaceWorkItemTemplate, + UpdateWorkspaceWorkItemTemplate, + WorkspaceWorkItemTemplate, +} from "../../models/WorkspaceTemplate"; + +/** + * WorkspaceWorkItemTemplates sub-resource + * Manages work item templates at the workspace level + */ +export class WorkItems extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/workitems/templates/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkspaceWorkItemTemplate): Promise { + return this.post(`/workspaces/${workspaceSlug}/workitems/templates/`, data); + } + + async update( + workspaceSlug: string, + templateId: string, + data: UpdateWorkspaceWorkItemTemplate + ): Promise { + return this.patch( + `/workspaces/${workspaceSlug}/workitems/templates/${templateId}/`, + data + ); + } + + async del(workspaceSlug: string, templateId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/workitems/templates/${templateId}/`); + } +} diff --git a/src/api/WorkspaceTemplates/index.ts b/src/api/WorkspaceTemplates/index.ts new file mode 100644 index 0000000..335391e --- /dev/null +++ b/src/api/WorkspaceTemplates/index.ts @@ -0,0 +1,22 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { WorkItems } from "./WorkItems"; +import { Projects } from "./Projects"; +import { Pages } from "./Pages"; + +/** + * WorkspaceTemplates API resource + * Container for workspace-level template sub-resources + */ +export class WorkspaceTemplates extends BaseResource { + public workItems: WorkItems; + public projects: Projects; + public pages: Pages; + + constructor(config: Configuration) { + super(config); + this.workItems = new WorkItems(config); + this.projects = new Projects(config); + this.pages = new Pages(config); + } +} diff --git a/src/api/WorkspaceWorkItemProperties/Options.ts b/src/api/WorkspaceWorkItemProperties/Options.ts new file mode 100644 index 0000000..a52b1fa --- /dev/null +++ b/src/api/WorkspaceWorkItemProperties/Options.ts @@ -0,0 +1,47 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { + CreateWorkItemPropertyOption, + UpdateWorkItemPropertyOption, + WorkItemPropertyOption, +} from "../../models/WorkItemProperty"; + +/** + * WorkspaceWorkItemPropertyOptions sub-resource + * Manages options on workspace-level work item properties + */ +export class Options extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string, propertyId: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/work-item-properties/${propertyId}/options/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create( + workspaceSlug: string, + propertyId: string, + data: CreateWorkItemPropertyOption + ): Promise { + return this.post( + `/workspaces/${workspaceSlug}/work-item-properties/${propertyId}/options/`, + data + ); + } + + async update( + workspaceSlug: string, + propertyId: string, + optionId: string, + data: UpdateWorkItemPropertyOption + ): Promise { + return this.patch( + `/workspaces/${workspaceSlug}/work-item-properties/${propertyId}/options/${optionId}/`, + data + ); + } +} diff --git a/src/api/WorkspaceWorkItemProperties/index.ts b/src/api/WorkspaceWorkItemProperties/index.ts new file mode 100644 index 0000000..eb6b5d3 --- /dev/null +++ b/src/api/WorkspaceWorkItemProperties/index.ts @@ -0,0 +1,36 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { CreateWorkItemProperty, UpdateWorkItemProperty, WorkItemProperty } from "../../models/WorkItemProperty"; +import { Options } from "./Options"; + +/** + * WorkspaceWorkItemProperties API resource + * Manages work item properties at the workspace level + */ +export class WorkspaceWorkItemProperties extends BaseResource { + public options: Options; + + constructor(config: Configuration) { + super(config); + this.options = new Options(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/work-item-properties/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkItemProperty): Promise { + return this.post(`/workspaces/${workspaceSlug}/work-item-properties/`, data); + } + + async update(workspaceSlug: string, propertyId: string, data: UpdateWorkItemProperty): Promise { + return this.patch(`/workspaces/${workspaceSlug}/work-item-properties/${propertyId}/`, data); + } + + async del(workspaceSlug: string, propertyId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/work-item-properties/${propertyId}/`); + } +} diff --git a/src/api/WorkspaceWorkItemTypes/Properties.ts b/src/api/WorkspaceWorkItemTypes/Properties.ts new file mode 100644 index 0000000..76c8825 --- /dev/null +++ b/src/api/WorkspaceWorkItemTypes/Properties.ts @@ -0,0 +1,33 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { WorkItemProperty } from "../../models/WorkItemProperty"; +import { WorkspaceWorkItemTypePropertyLink } from "../../models/WorkItemType"; + +/** + * WorkspaceWorkItemTypeProperties sub-resource + * Manages property links on workspace-level work item types + */ +export class Properties extends BaseResource { + constructor(config: Configuration) { + super(config); + } + + async list(workspaceSlug: string, typeId: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/work-item-types/${typeId}/properties/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create( + workspaceSlug: string, + typeId: string, + data: WorkspaceWorkItemTypePropertyLink + ): Promise { + return this.post(`/workspaces/${workspaceSlug}/work-item-types/${typeId}/properties/`, data); + } + + async del(workspaceSlug: string, typeId: string, propertyId: string): Promise { + return this.httpDelete(`/workspaces/${workspaceSlug}/work-item-types/${typeId}/properties/${propertyId}/`); + } +} diff --git a/src/api/WorkspaceWorkItemTypes/index.ts b/src/api/WorkspaceWorkItemTypes/index.ts new file mode 100644 index 0000000..ad65092 --- /dev/null +++ b/src/api/WorkspaceWorkItemTypes/index.ts @@ -0,0 +1,32 @@ +import { BaseResource } from "../BaseResource"; +import { Configuration } from "../../Configuration"; +import { WorkItemType, CreateWorkItemType, UpdateWorkItemType } from "../../models/WorkItemType"; +import { Properties } from "./Properties"; + +/** + * WorkspaceWorkItemTypes API resource + * Manages work item types at the workspace level + */ +export class WorkspaceWorkItemTypes extends BaseResource { + public properties: Properties; + + constructor(config: Configuration) { + super(config); + this.properties = new Properties(config); + } + + async list(workspaceSlug: string): Promise { + const data = await this.get( + `/workspaces/${workspaceSlug}/work-item-types/` + ); + return Array.isArray(data) ? data : data.results; + } + + async create(workspaceSlug: string, data: CreateWorkItemType): Promise { + return this.post(`/workspaces/${workspaceSlug}/work-item-types/`, data); + } + + async update(workspaceSlug: string, typeId: string, data: UpdateWorkItemType): Promise { + return this.patch(`/workspaces/${workspaceSlug}/work-item-types/${typeId}/`, data); + } +} diff --git a/src/client/plane-client.ts b/src/client/plane-client.ts index 8245222..39e236b 100644 --- a/src/client/plane-client.ts +++ b/src/client/plane-client.ts @@ -20,6 +20,13 @@ import { Teamspaces } from "../api/Teamspaces"; import { Initiatives } from "../api/Initiatives"; import { Milestones } from "../api/Milestones"; import { AgentRuns } from "../api/AgentRuns"; +import { WorkspaceTemplates } from "../api/WorkspaceTemplates"; +import { WorkspaceWorkItemTypes } from "../api/WorkspaceWorkItemTypes"; +import { WorkspaceWorkItemProperties } from "../api/WorkspaceWorkItemProperties"; +import { WorkspaceProjectLabels } from "../api/WorkspaceProjectLabels"; +import { WorkspaceProjectStates } from "../api/WorkspaceProjectStates"; +import { WorkItemRelationDefinitions } from "../api/WorkItemRelationDefinitions"; +import { Releases } from "../api/Releases"; /** * Main Plane Client class @@ -48,6 +55,13 @@ export class PlaneClient { public milestones: Milestones; public initiatives: Initiatives; public agentRuns: AgentRuns; + public workspaceTemplates: WorkspaceTemplates; + public workspaceWorkItemTypes: WorkspaceWorkItemTypes; + public workspaceWorkItemProperties: WorkspaceWorkItemProperties; + public workspaceProjectLabels: WorkspaceProjectLabels; + public workspaceProjectStates: WorkspaceProjectStates; + public workItemRelationDefinitions: WorkItemRelationDefinitions; + public releases: Releases; constructor(config: { baseUrl?: string; apiKey?: string; accessToken?: string; enableLogging?: boolean }) { this.config = new Configuration({ @@ -82,5 +96,12 @@ export class PlaneClient { this.milestones = new Milestones(this.config); this.initiatives = new Initiatives(this.config); this.agentRuns = new AgentRuns(this.config); + this.workspaceTemplates = new WorkspaceTemplates(this.config); + this.workspaceWorkItemTypes = new WorkspaceWorkItemTypes(this.config); + this.workspaceWorkItemProperties = new WorkspaceWorkItemProperties(this.config); + this.workspaceProjectLabels = new WorkspaceProjectLabels(this.config); + this.workspaceProjectStates = new WorkspaceProjectStates(this.config); + this.workItemRelationDefinitions = new WorkItemRelationDefinitions(this.config); + this.releases = new Releases(this.config); } } diff --git a/src/index.ts b/src/index.ts index 4b17201..15bd040 100644 --- a/src/index.ts +++ b/src/index.ts @@ -32,6 +32,13 @@ export { Teamspaces } from "./api/Teamspaces"; export { Milestones } from "./api/Milestones"; export { Initiatives } from "./api/Initiatives"; export { AgentRuns } from "./api/AgentRuns"; +export { WorkspaceTemplates } from "./api/WorkspaceTemplates"; +export { WorkspaceWorkItemTypes } from "./api/WorkspaceWorkItemTypes"; +export { WorkspaceWorkItemProperties } from "./api/WorkspaceWorkItemProperties"; +export { WorkspaceProjectLabels } from "./api/WorkspaceProjectLabels"; +export { WorkspaceProjectStates } from "./api/WorkspaceProjectStates"; +export { WorkItemRelationDefinitions } from "./api/WorkItemRelationDefinitions"; +export { Releases } from "./api/Releases"; // Sub-resources export { Relations as WorkItemRelations } from "./api/WorkItems/Relations"; @@ -49,6 +56,14 @@ 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 { WorkItems as WorkspaceWorkItemTemplates } from "./api/WorkspaceTemplates/WorkItems"; +export { Projects as WorkspaceProjectTemplates } from "./api/WorkspaceTemplates/Projects"; +export { Pages as WorkspacePageTemplates } from "./api/WorkspaceTemplates/Pages"; +export { Properties as WorkspaceWorkItemTypeProperties } from "./api/WorkspaceWorkItemTypes/Properties"; +export { Options as WorkspaceWorkItemPropertyOptions } from "./api/WorkspaceWorkItemProperties/Options"; +export { Tags as ReleaseTags } from "./api/Releases/Tags"; +export { Labels as ReleaseLabels } from "./api/Releases/Labels"; +export { ItemLabels as ReleaseItemLabels } from "./api/Releases/ItemLabels"; // Models export * from "./models"; diff --git a/src/models/Release.ts b/src/models/Release.ts new file mode 100644 index 0000000..b61acc8 --- /dev/null +++ b/src/models/Release.ts @@ -0,0 +1,49 @@ +import { BaseModel } from "./common"; + +/** + * Release model interfaces + */ +export interface Release extends BaseModel { + name: string; + description?: string; + start_date?: string; + release_date?: string; + status?: string; + logo_props?: Record; + workspace: string; +} + +export type CreateRelease = Pick< + Release, + "name" | "description" | "start_date" | "release_date" | "status" | "logo_props" +>; + +export type UpdateRelease = Partial; + +// ─── Release Tags ──────────────────────────────────────────────────────────── + +export interface ReleaseTag extends BaseModel { + name: string; + color?: string; + sort_order?: number; + workspace: string; +} + +export type CreateReleaseTag = Pick; + +// ─── Release Labels ────────────────────────────────────────────────────────── + +export interface ReleaseLabel extends BaseModel { + name: string; + color?: string; + sort_order?: number; + workspace: string; +} + +export type CreateReleaseLabel = Pick; + +// ─── Release Item Labels ───────────────────────────────────────────────────── + +export interface AddReleaseItemLabel { + label_ids: string[]; +} diff --git a/src/models/WorkItemRelationDefinition.ts b/src/models/WorkItemRelationDefinition.ts new file mode 100644 index 0000000..c9011f1 --- /dev/null +++ b/src/models/WorkItemRelationDefinition.ts @@ -0,0 +1,27 @@ +import { BaseModel } from "./common"; + +/** + * Work item relation definition model interfaces + */ +export interface WorkItemRelationDefinition extends BaseModel { + name: string; + outward?: string; + inward?: string; + is_default?: boolean; + is_active?: boolean; + color?: string; + sort_order?: number; + workspace: string; +} + +export type CreateWorkItemRelationDefinition = Pick< + WorkItemRelationDefinition, + "name" | "outward" | "inward" | "is_default" | "is_active" | "color" | "sort_order" +>; + +export type UpdateWorkItemRelationDefinition = Partial; + +export interface ListWorkItemRelationDefinitionsParams { + is_default?: boolean; + is_active?: boolean; +} diff --git a/src/models/WorkItemType.ts b/src/models/WorkItemType.ts index 8f2ee92..0c8d7cc 100644 --- a/src/models/WorkItemType.ts +++ b/src/models/WorkItemType.ts @@ -18,3 +18,10 @@ export interface WorkItemType extends BaseModel { export type CreateWorkItemType = Partial; export type UpdateWorkItemType = Partial; + +/** + * DTO for linking a property to a workspace work item type + */ +export interface WorkspaceWorkItemTypePropertyLink { + property_id: string; +} diff --git a/src/models/WorkspaceProjectLabel.ts b/src/models/WorkspaceProjectLabel.ts new file mode 100644 index 0000000..a3952f5 --- /dev/null +++ b/src/models/WorkspaceProjectLabel.ts @@ -0,0 +1,15 @@ +import { BaseModel } from "./common"; + +/** + * Workspace project label model interfaces + */ +export interface WorkspaceProjectLabel extends BaseModel { + name: string; + color?: string; + sort_order?: number; + workspace: string; +} + +export type CreateWorkspaceProjectLabel = Pick; + +export type UpdateWorkspaceProjectLabel = Partial; diff --git a/src/models/WorkspaceProjectState.ts b/src/models/WorkspaceProjectState.ts new file mode 100644 index 0000000..d411997 --- /dev/null +++ b/src/models/WorkspaceProjectState.ts @@ -0,0 +1,21 @@ +import { BaseModel } from "./common"; + +/** + * Workspace project state model interfaces + */ +export interface WorkspaceProjectState extends BaseModel { + name: string; + color?: string; + group?: string; + sequence?: number; + default?: boolean; + description?: string; + workspace: string; +} + +export type CreateWorkspaceProjectState = Pick< + WorkspaceProjectState, + "name" | "color" | "group" | "sequence" | "default" | "description" +>; + +export type UpdateWorkspaceProjectState = Partial; diff --git a/src/models/WorkspaceTemplate.ts b/src/models/WorkspaceTemplate.ts new file mode 100644 index 0000000..a7d24d4 --- /dev/null +++ b/src/models/WorkspaceTemplate.ts @@ -0,0 +1,59 @@ +import { BaseModel } from "./common"; + +/** + * Workspace-level template model interfaces + * These are distinct from project-scoped templates (ProjectTemplate.ts) + */ + +// ─── Work Item Templates ───────────────────────────────────────────────────── + +export interface WorkspaceWorkItemTemplate extends BaseModel { + name: string; + description?: string; + description_html?: string; + template_data?: Record; + logo_props?: Record; + workspace: string; +} + +export type CreateWorkspaceWorkItemTemplate = Pick< + WorkspaceWorkItemTemplate, + "name" | "description" | "description_html" | "template_data" | "logo_props" +>; + +export type UpdateWorkspaceWorkItemTemplate = Partial; + +// ─── Project Templates ─────────────────────────────────────────────────────── + +export interface WorkspaceProjectTemplate extends BaseModel { + name: string; + description?: string; + logo_props?: Record; + template_data?: Record; + workspace: string; +} + +export type CreateWorkspaceProjectTemplate = Pick< + WorkspaceProjectTemplate, + "name" | "description" | "logo_props" | "template_data" +>; + +export type UpdateWorkspaceProjectTemplate = Partial; + +// ─── Page Templates ────────────────────────────────────────────────────────── + +export interface WorkspacePageTemplate extends BaseModel { + name: string; + description?: string; + description_html?: string; + template_data?: Record; + logo_props?: Record; + workspace: string; +} + +export type CreateWorkspacePageTemplate = Pick< + WorkspacePageTemplate, + "name" | "description" | "description_html" | "template_data" | "logo_props" +>; + +export type UpdateWorkspacePageTemplate = Partial; diff --git a/src/models/index.ts b/src/models/index.ts index ce61a01..e9f5bce 100644 --- a/src/models/index.ts +++ b/src/models/index.ts @@ -26,3 +26,8 @@ export * from "./WorkItemRelation"; export * from "./WorkItemType"; export * from "./WorkLog"; export * from "./WorkspaceFeatures"; +export * from "./WorkspaceTemplate"; +export * from "./WorkspaceProjectLabel"; +export * from "./WorkspaceProjectState"; +export * from "./WorkItemRelationDefinition"; +export * from "./Release"; diff --git a/tests/unit/releases/releases.test.ts b/tests/unit/releases/releases.test.ts new file mode 100644 index 0000000..d0e223e --- /dev/null +++ b/tests/unit/releases/releases.test.ts @@ -0,0 +1,103 @@ +import { PlaneClient } from "../../../src/client/plane-client"; +import { Release, ReleaseLabel, ReleaseTag } from "../../../src/models"; +import { config } from "../constants"; +import { createTestClient, randomizeName } from "../../helpers/test-utils"; +import { describeIf as describe } from "../../helpers/conditional-tests"; + +describe(!!config.workspaceSlug, "Releases API Tests", () => { + let client: PlaneClient; + let workspaceSlug: string; + let release: Release; + let tag: ReleaseTag; + let label: ReleaseLabel; + + beforeAll(async () => { + client = createTestClient(); + workspaceSlug = config.workspaceSlug; + }); + + afterAll(async () => { + // Note: Releases API may not expose a delete endpoint — skip if absent + }); + + // ─── Releases ──────────────────────────────────────────────────────────────── + + it("should create a release", async () => { + release = await client.releases.create(workspaceSlug, { + name: randomizeName("Test Release"), + }); + expect(release).toBeDefined(); + expect(release.id).toBeDefined(); + expect(release.name).toContain("Test Release"); + expect(release.workspace).toBeDefined(); + }); + + it("should list releases", async () => { + const releases = await client.releases.list(workspaceSlug); + expect(Array.isArray(releases)).toBe(true); + expect(releases.find((r) => r.id === release.id)).toBeDefined(); + }); + + it("should update a release", async () => { + const updated = await client.releases.update(workspaceSlug, release.id!, { + name: randomizeName("Updated Release"), + }); + expect(updated.id).toBe(release.id); + expect(updated.name).toContain("Updated Release"); + release = updated; + }); + + // ─── Release Tags ───────────────────────────────────────────────────────── + + it("should create a release tag", async () => { + tag = await client.releases.tags.create(workspaceSlug, { + name: randomizeName("Test Tag"), + color: "#FF5733", + }); + expect(tag).toBeDefined(); + expect(tag.id).toBeDefined(); + expect(tag.name).toContain("Test Tag"); + }); + + it("should list release tags", async () => { + const tags = await client.releases.tags.list(workspaceSlug); + expect(Array.isArray(tags)).toBe(true); + expect(tags.find((t) => t.id === tag.id)).toBeDefined(); + }); + + // ─── Release Labels ─────────────────────────────────────────────────────── + + it("should create a release label", async () => { + label = await client.releases.labels.create(workspaceSlug, { + name: randomizeName("Test Label"), + color: "#33AAFF", + }); + expect(label).toBeDefined(); + expect(label.id).toBeDefined(); + expect(label.name).toContain("Test Label"); + }); + + it("should list release labels", async () => { + const labels = await client.releases.labels.list(workspaceSlug); + expect(Array.isArray(labels)).toBe(true); + expect(labels.find((l) => l.id === label.id)).toBeDefined(); + }); + + // ─── Release Item Labels ────────────────────────────────────────────────── + + it("should add a label to a release", async () => { + const added = await client.releases.itemLabels.create(workspaceSlug, release.id!, { + label_ids: [label.id!], + }); + expect(Array.isArray(added)).toBe(true); + }); + + it("should list labels on a release", async () => { + const itemLabels = await client.releases.itemLabels.list(workspaceSlug, release.id!); + expect(Array.isArray(itemLabels)).toBe(true); + }); + + it("should remove a label from a release", async () => { + await expect(client.releases.itemLabels.del(workspaceSlug, release.id!, label.id!)).resolves.toBeUndefined(); + }); +}); diff --git a/tests/unit/work-item-relation-definitions.test.ts b/tests/unit/work-item-relation-definitions.test.ts new file mode 100644 index 0000000..0cb85bf --- /dev/null +++ b/tests/unit/work-item-relation-definitions.test.ts @@ -0,0 +1,64 @@ +import { PlaneClient } from "../../src/client/plane-client"; +import { WorkItemRelationDefinition } from "../../src/models"; +import { config } from "./constants"; +import { createTestClient, randomizeName } from "../helpers/test-utils"; +import { describeIf as describe } from "../helpers/conditional-tests"; + +describe(!!config.workspaceSlug, "WorkItemRelationDefinitions API Tests", () => { + let client: PlaneClient; + let workspaceSlug: string; + let definition: WorkItemRelationDefinition; + + beforeAll(async () => { + client = createTestClient(); + workspaceSlug = config.workspaceSlug; + }); + + afterAll(async () => { + if (definition?.id) { + try { + await client.workItemRelationDefinitions.del(workspaceSlug, definition.id); + } catch (error) { + console.warn("Failed to delete work item relation definition:", error); + } + } + }); + + it("should list work item relation definitions", async () => { + const definitions = await client.workItemRelationDefinitions.list(workspaceSlug); + expect(definitions).toBeDefined(); + expect(Array.isArray(definitions)).toBe(true); + }); + + it("should filter by is_default", async () => { + const defaults = await client.workItemRelationDefinitions.list(workspaceSlug, { is_default: true }); + expect(Array.isArray(defaults)).toBe(true); + defaults.forEach((d) => expect(d.is_default).toBe(true)); + }); + + it("should create a work item relation definition", async () => { + definition = await client.workItemRelationDefinitions.create(workspaceSlug, { + name: randomizeName("WI Relation"), + outward: "blocks", + inward: "blocked by", + is_active: true, + }); + expect(definition).toBeDefined(); + expect(definition.id).toBeDefined(); + expect(definition.name).toContain("WI Relation"); + }); + + it("should update a work item relation definition", async () => { + const updated = await client.workItemRelationDefinitions.update(workspaceSlug, definition.id!, { + name: randomizeName("Updated WI Relation"), + }); + expect(updated.id).toBe(definition.id); + expect(updated.name).toContain("Updated WI Relation"); + definition = updated; + }); + + it("should delete a work item relation definition", async () => { + await expect(client.workItemRelationDefinitions.del(workspaceSlug, definition.id!)).resolves.toBeUndefined(); + definition = { ...definition, id: undefined } as unknown as WorkItemRelationDefinition; + }); +}); diff --git a/tests/unit/workspace-project-labels.test.ts b/tests/unit/workspace-project-labels.test.ts new file mode 100644 index 0000000..e1c9eac --- /dev/null +++ b/tests/unit/workspace-project-labels.test.ts @@ -0,0 +1,57 @@ +import { PlaneClient } from "../../src/client/plane-client"; +import { WorkspaceProjectLabel } from "../../src/models"; +import { config } from "./constants"; +import { createTestClient, randomizeName } from "../helpers/test-utils"; +import { describeIf as describe } from "../helpers/conditional-tests"; + +describe(!!config.workspaceSlug, "WorkspaceProjectLabels API Tests", () => { + let client: PlaneClient; + let workspaceSlug: string; + let label: WorkspaceProjectLabel; + + beforeAll(async () => { + client = createTestClient(); + workspaceSlug = config.workspaceSlug; + }); + + afterAll(async () => { + if (label?.id) { + try { + await client.workspaceProjectLabels.del(workspaceSlug, label.id); + } catch (error) { + console.warn("Failed to delete workspace project label:", error); + } + } + }); + + it("should create a workspace project label", async () => { + label = await client.workspaceProjectLabels.create(workspaceSlug, { + name: randomizeName("WS Label"), + color: "#FF5733", + }); + expect(label).toBeDefined(); + expect(label.id).toBeDefined(); + expect(label.name).toContain("WS Label"); + expect(label.color).toBe("#FF5733"); + }); + + it("should list workspace project labels", async () => { + const labels = await client.workspaceProjectLabels.list(workspaceSlug); + expect(Array.isArray(labels)).toBe(true); + expect(labels.find((l) => l.id === label.id)).toBeDefined(); + }); + + it("should update a workspace project label", async () => { + const updated = await client.workspaceProjectLabels.update(workspaceSlug, label.id!, { + name: randomizeName("Updated WS Label"), + }); + expect(updated.id).toBe(label.id); + expect(updated.name).toContain("Updated WS Label"); + label = updated; + }); + + it("should delete a workspace project label", async () => { + await expect(client.workspaceProjectLabels.del(workspaceSlug, label.id!)).resolves.toBeUndefined(); + label = { ...label, id: undefined } as unknown as WorkspaceProjectLabel; + }); +}); diff --git a/tests/unit/workspace-project-states.test.ts b/tests/unit/workspace-project-states.test.ts new file mode 100644 index 0000000..2c2dbe2 --- /dev/null +++ b/tests/unit/workspace-project-states.test.ts @@ -0,0 +1,57 @@ +import { PlaneClient } from "../../src/client/plane-client"; +import { WorkspaceProjectState } from "../../src/models"; +import { config } from "./constants"; +import { createTestClient, randomizeName } from "../helpers/test-utils"; +import { describeIf as describe } from "../helpers/conditional-tests"; + +describe(!!config.workspaceSlug, "WorkspaceProjectStates API Tests", () => { + let client: PlaneClient; + let workspaceSlug: string; + let state: WorkspaceProjectState; + + beforeAll(async () => { + client = createTestClient(); + workspaceSlug = config.workspaceSlug; + }); + + afterAll(async () => { + if (state?.id) { + try { + await client.workspaceProjectStates.del(workspaceSlug, state.id); + } catch (error) { + console.warn("Failed to delete workspace project state:", error); + } + } + }); + + it("should create a workspace project state", async () => { + state = await client.workspaceProjectStates.create(workspaceSlug, { + name: randomizeName("WS State"), + color: "#3A3A3A", + group: "started", + }); + expect(state).toBeDefined(); + expect(state.id).toBeDefined(); + expect(state.name).toContain("WS State"); + }); + + it("should list workspace project states", async () => { + const states = await client.workspaceProjectStates.list(workspaceSlug); + expect(Array.isArray(states)).toBe(true); + expect(states.find((s) => s.id === state.id)).toBeDefined(); + }); + + it("should update a workspace project state", async () => { + const updated = await client.workspaceProjectStates.update(workspaceSlug, state.id!, { + name: randomizeName("Updated WS State"), + }); + expect(updated.id).toBe(state.id); + expect(updated.name).toContain("Updated WS State"); + state = updated; + }); + + it("should delete a workspace project state", async () => { + await expect(client.workspaceProjectStates.del(workspaceSlug, state.id!)).resolves.toBeUndefined(); + state = { ...state, id: undefined } as unknown as WorkspaceProjectState; + }); +}); diff --git a/tests/unit/workspace-templates/workspace-templates.test.ts b/tests/unit/workspace-templates/workspace-templates.test.ts new file mode 100644 index 0000000..69d4e99 --- /dev/null +++ b/tests/unit/workspace-templates/workspace-templates.test.ts @@ -0,0 +1,138 @@ +import { PlaneClient } from "../../../src/client/plane-client"; +import { WorkspaceWorkItemTemplate, WorkspaceProjectTemplate, WorkspacePageTemplate } from "../../../src/models"; +import { config } from "../constants"; +import { createTestClient, randomizeName } from "../../helpers/test-utils"; +import { describeIf as describe } from "../../helpers/conditional-tests"; + +describe(!!config.workspaceSlug, "WorkspaceTemplates API Tests", () => { + let client: PlaneClient; + let workspaceSlug: string; + + let workItemTemplate: WorkspaceWorkItemTemplate; + let projectTemplate: WorkspaceProjectTemplate; + let pageTemplate: WorkspacePageTemplate; + + beforeAll(async () => { + client = createTestClient(); + workspaceSlug = config.workspaceSlug; + }); + + afterAll(async () => { + if (workItemTemplate?.id) { + try { + await client.workspaceTemplates.workItems.del(workspaceSlug, workItemTemplate.id); + } catch (error) { + console.warn("Failed to delete workspace work item template:", error); + } + } + if (projectTemplate?.id) { + try { + await client.workspaceTemplates.projects.del(workspaceSlug, projectTemplate.id); + } catch (error) { + console.warn("Failed to delete workspace project template:", error); + } + } + if (pageTemplate?.id) { + try { + await client.workspaceTemplates.pages.del(workspaceSlug, pageTemplate.id); + } catch (error) { + console.warn("Failed to delete workspace page template:", error); + } + } + }); + + // ─── Work Item Templates ───────────────────────────────────────────────────── + + it("should create a workspace work item template", async () => { + workItemTemplate = await client.workspaceTemplates.workItems.create(workspaceSlug, { + name: randomizeName("WS WI Template"), + }); + expect(workItemTemplate).toBeDefined(); + expect(workItemTemplate.id).toBeDefined(); + expect(workItemTemplate.name).toContain("WS WI Template"); + expect(workItemTemplate.workspace).toBeDefined(); + }); + + it("should list workspace work item templates", async () => { + const templates = await client.workspaceTemplates.workItems.list(workspaceSlug); + expect(Array.isArray(templates)).toBe(true); + expect(templates.length).toBeGreaterThan(0); + expect(templates.find((t) => t.id === workItemTemplate.id)).toBeDefined(); + }); + + it("should update a workspace work item template", async () => { + const updated = await client.workspaceTemplates.workItems.update(workspaceSlug, workItemTemplate.id!, { + name: randomizeName("Updated WS WI Template"), + }); + expect(updated.id).toBe(workItemTemplate.id); + expect(updated.name).toContain("Updated WS WI Template"); + workItemTemplate = updated; + }); + + it("should delete a workspace work item template", async () => { + await expect(client.workspaceTemplates.workItems.del(workspaceSlug, workItemTemplate.id!)).resolves.toBeUndefined(); + workItemTemplate = { ...workItemTemplate, id: undefined } as unknown as WorkspaceWorkItemTemplate; + }); + + // ─── Project Templates ─────────────────────────────────────────────────────── + + it("should create a workspace project template", async () => { + projectTemplate = await client.workspaceTemplates.projects.create(workspaceSlug, { + name: randomizeName("WS Project Template"), + }); + expect(projectTemplate).toBeDefined(); + expect(projectTemplate.id).toBeDefined(); + expect(projectTemplate.name).toContain("WS Project Template"); + }); + + it("should list workspace project templates", async () => { + const templates = await client.workspaceTemplates.projects.list(workspaceSlug); + expect(Array.isArray(templates)).toBe(true); + expect(templates.find((t) => t.id === projectTemplate.id)).toBeDefined(); + }); + + it("should update a workspace project template", async () => { + const updated = await client.workspaceTemplates.projects.update(workspaceSlug, projectTemplate.id!, { + name: randomizeName("Updated WS Project Template"), + }); + expect(updated.id).toBe(projectTemplate.id); + expect(updated.name).toContain("Updated WS Project Template"); + projectTemplate = updated; + }); + + it("should delete a workspace project template", async () => { + await expect(client.workspaceTemplates.projects.del(workspaceSlug, projectTemplate.id!)).resolves.toBeUndefined(); + projectTemplate = { ...projectTemplate, id: undefined } as unknown as WorkspaceProjectTemplate; + }); + + // ─── Page Templates ────────────────────────────────────────────────────────── + + it("should create a workspace page template", async () => { + pageTemplate = await client.workspaceTemplates.pages.create(workspaceSlug, { + name: randomizeName("WS Page Template"), + }); + expect(pageTemplate).toBeDefined(); + expect(pageTemplate.id).toBeDefined(); + expect(pageTemplate.name).toContain("WS Page Template"); + }); + + it("should list workspace page templates", async () => { + const templates = await client.workspaceTemplates.pages.list(workspaceSlug); + expect(Array.isArray(templates)).toBe(true); + expect(templates.find((t) => t.id === pageTemplate.id)).toBeDefined(); + }); + + it("should update a workspace page template", async () => { + const updated = await client.workspaceTemplates.pages.update(workspaceSlug, pageTemplate.id!, { + name: randomizeName("Updated WS Page Template"), + }); + expect(updated.id).toBe(pageTemplate.id); + expect(updated.name).toContain("Updated WS Page Template"); + pageTemplate = updated; + }); + + it("should delete a workspace page template", async () => { + await expect(client.workspaceTemplates.pages.del(workspaceSlug, pageTemplate.id!)).resolves.toBeUndefined(); + pageTemplate = { ...pageTemplate, id: undefined } as unknown as WorkspacePageTemplate; + }); +}); diff --git a/tests/unit/workspace-work-item-types.test.ts b/tests/unit/workspace-work-item-types.test.ts new file mode 100644 index 0000000..2872df2 --- /dev/null +++ b/tests/unit/workspace-work-item-types.test.ts @@ -0,0 +1,50 @@ +import { PlaneClient } from "../../src/client/plane-client"; +import { WorkItemType } from "../../src/models"; +import { config } from "./constants"; +import { createTestClient, randomizeName } from "../helpers/test-utils"; +import { describeIf as describe } from "../helpers/conditional-tests"; + +describe(!!config.workspaceSlug, "WorkspaceWorkItemTypes API Tests", () => { + let client: PlaneClient; + let workspaceSlug: string; + let workItemType: WorkItemType; + + beforeAll(async () => { + client = createTestClient(); + workspaceSlug = config.workspaceSlug; + }); + + afterAll(async () => { + // Note: workspace work item types may not support delete via API — skip if needed + }); + + it("should list workspace work item types", async () => { + const types = await client.workspaceWorkItemTypes.list(workspaceSlug); + expect(types).toBeDefined(); + expect(Array.isArray(types)).toBe(true); + }); + + it("should create a workspace work item type", async () => { + workItemType = await client.workspaceWorkItemTypes.create(workspaceSlug, { + name: randomizeName("WS WI Type"), + }); + expect(workItemType).toBeDefined(); + expect(workItemType.id).toBeDefined(); + expect(workItemType.name).toContain("WS WI Type"); + }); + + it("should update a workspace work item type", async () => { + const updated = await client.workspaceWorkItemTypes.update(workspaceSlug, workItemType.id!, { + name: randomizeName("Updated WS WI Type"), + }); + expect(updated.id).toBe(workItemType.id); + expect(updated.name).toContain("Updated WS WI Type"); + workItemType = updated; + }); + + it("should list properties for a workspace work item type", async () => { + const properties = await client.workspaceWorkItemTypes.properties.list(workspaceSlug, workItemType.id!); + expect(properties).toBeDefined(); + expect(Array.isArray(properties)).toBe(true); + }); +});