Skip to content

Commit

Permalink
24: Add support for epics (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
JensAstrup authored Mar 28, 2024
1 parent b241395 commit f2d2dc1
Show file tree
Hide file tree
Showing 21 changed files with 386 additions and 138 deletions.
2 changes: 1 addition & 1 deletion docs/assets/search.js

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/classes/Client.html

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions docs/classes/Iteration.html

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions docs/classes/IterationsService.html

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions docs/classes/Member.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/classes/MembersService.html

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions docs/classes/StoriesService.html

Large diffs are not rendered by default.

34 changes: 18 additions & 16 deletions docs/classes/Story.html

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions docs/classes/Team.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/classes/TeamService.html

Large diffs are not rendered by default.

18 changes: 9 additions & 9 deletions docs/classes/Workflow.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/classes/WorkflowsService.html

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
42 changes: 42 additions & 0 deletions src/epics/contracts/epic-api-data.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import BaseData from '@sx/base-data'
import UUID from '@sx/utils/uuid'


export default interface EpicApiData extends BaseData {
app_url: string
archived: boolean
associated_groups: []
completed: boolean
completed_at: string | null
completed_at_override: string | null
deadline: string | null
description: string
entity_type: string
epic_state_id: number
external_id: string | null
follower_ids: UUID[]
group_ids: UUID[]
id: number
label_ids: number[]
labels: []
member_mention_ids: UUID[]
mention_ids: UUID[]
milestone_id: number | null
name: string
objective_ids: number[]
owner_ids: UUID[]
planned_start_date: string | null
position: number
productboard_id: UUID | null
productboard_name: string | null
productboard_plugin_id: UUID | null
productboard_url: string | null
project_ids: number[]
requested_by_id: UUID
started: boolean
started_at: string | null
started_at_override: string | null
stats: object
stories_without_projects: number
updated_at: string | null
}
41 changes: 41 additions & 0 deletions src/epics/contracts/epic-interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import UUID from '@sx/utils/uuid'


export default interface EpicInterface {
appUrl: string
archived: boolean
associatedGroups: []
completed: boolean
completedAt: string | null
completedAtOverride: string | null
deadline: string | null
description: string
entityType: string
epicStateId: number
externalId: string | null
followerIds: UUID[]
groupIds: UUID[]
id: number
labelIds: number[]
labels: []
memberMentionIds: UUID[]
mentionIds: UUID[]
milestoneId: number | null
name: string
objectiveIds: number[]
ownerIds: UUID[]
plannedStartDate: string | null
position: number
productboardId: UUID | null
productboardName: string | null
productboardPluginId: UUID | null
productboardUrl: string | null
projectIds: number[]
requestedById: UUID
started: boolean
startedAt: string | null
startedAtOverride: string | null
stats: object
storiesWithoutProjects: number
updatedAt: string | null
}
74 changes: 74 additions & 0 deletions src/epics/epic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import ShortcutResource from '@sx/base-resource'
import IterationInterface from '@sx/iterations/contracts/iteration-interface'
import Member from '@sx/members/member'
import MembersService from '@sx/members/members-service'
import Team from '@sx/teams/team'
import TeamService from '@sx/teams/team-service'
import {getHeaders} from '@sx/utils/headers'
import UUID from '@sx/utils/uuid'


export default class Epic extends ShortcutResource {
public static baseUrl: string = 'https://api.app.shortcut.com/api/v3/epics'

constructor(init: IterationInterface | object) {
super()
Object.assign(this, init)
this.changedFields = []
}

/**
* Get the teams assigned to the story, labelled as "Group" in the Shortcut API
* @returns {Promise<Team>}
*/
get teams(): Promise<Team[]> {
const service = new TeamService({headers: getHeaders()})
return service.getMany(this.groupIds)
}

/**
* Get the members following the epic
* @returns {Promise<Member[]>}
*/
get followers(): Promise<Member[]> {
const service: MembersService = new MembersService({headers: getHeaders()})
return service.getMany(this.followerIds)
}

appUrl!: string
archived!: boolean
associatedGroups!: []
completed!: boolean
completedAt!: string | null
completedAtOverride!: string | null
deadline!: string | null
description!: string
entityType!: string
epicStateId!: number
externalId!: string | null
followerIds!: UUID[]
groupIds!: UUID[]
id!: number
labelIds!: number[]
labels!: []
memberMentionIds!: UUID[]
mentionIds!: UUID[]
milestoneId!: number | null
name!: string
objectiveIds!: number[]
ownerIds!: UUID[]
plannedStartDate!: string | null
position!: number
productboardId!: UUID | null
productboardName!: string | null
productboardPluginId!: UUID | null
productboardUrl!: string | null
projectIds!: number[]
requestedById!: UUID
started!: boolean
startedAt!: string | null
startedAtOverride!: string | null
stats!: object
storiesWithoutProjects!: number
updatedAt!: string | null
}
11 changes: 11 additions & 0 deletions src/epics/epics-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import BaseService from '@sx/base-service'
import Epic from '@sx/epics/epic'
import UUID from '@sx/utils/uuid'


export default class EpicsService extends BaseService<Epic> {
public baseUrl = 'https://api.app.shortcut.com/api/v3/epics'
protected factory = (data: object) => new Epic(data)
public static epics: Record<number, Epic> = {}

}
9 changes: 0 additions & 9 deletions src/iterations/iterations-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,4 @@ export default class IterationsService extends BaseService<Iteration> {
public baseUrl = 'https://api.app.shortcut.com/api/v3/iterations'
protected factory = (data: object) => new Iteration(data)
public static iterations: Record<number, Iteration> = {}

public async create(iteration: CreateIterationData): Promise<Iteration> {
const response = await axios.post(this.baseUrl, iteration, {headers: this.headers})
if (response.status >= 400) {
throw new Error('HTTP error ' + response.status)
}
const iterationData = convertApiFields(response.data) as IterationInterface
return new Iteration(iterationData)
}
}
26 changes: 20 additions & 6 deletions src/stories/story.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import ShortcutResource from '@sx/base-resource'
import Epic from '@sx/epics/epic'
import EpicsService from '@sx/epics/epics-service'
import HistoryApiData from '@sx/stories/history/contracts/history-api-data'
import HistoryInterface from '@sx/stories/history/contracts/history-interface'
import {WorkflowStateInterface} from '@sx/workflows/contracts/workflow-state-interface'
Expand Down Expand Up @@ -73,6 +75,18 @@ export default class Story extends ShortcutResource {
return service.getMany(this.ownerIds)
}

/**
* Get the epic of the story
* @returns {Promise<Epic>}
*/
get epic(): Promise<Epic> {
if (!this.epicId) {
throw new Error('Story does not have an epic')
}
const service = new EpicsService({headers: getHeaders()})
return service.get(this.epicId)
}

public async history(): Promise<HistoryInterface[]> {
const url = `${Story.baseUrl}/stories/${this.id}/history`
const response = await axios.get(url, {headers: getHeaders()}).catch((error) => {
Expand All @@ -83,9 +97,9 @@ export default class Story extends ShortcutResource {
}

/**
* Calculates the cycle time of a story in days.
* Calculates the cycle time of a story in hours.
*
* @returns {Promise<number>} - The cycle time in days.
* @returns {Promise<number>} - The cycle time in hours.
* @throws {Error} - If the story is not completed or has not been started.
*/
public async cycleTime(): Promise<number> {
Expand All @@ -96,13 +110,13 @@ export default class Story extends ShortcutResource {
throw new Error('Story does not have a cycle time')
}

return (completedAt.getTime() - startedAt.getTime()) / (60 * 60 * 24)
return (completedAt.getTime() - startedAt.getTime()) / (1000 * 60 * 60)
}

/**
* Calculates the time a story has been in development in days.
* Calculates the time a story has been in development in hours.
*
* @returns {Promise<number>} - The time in development in days.
* @returns {Promise<number>} - The time in development in hours.
* @throws {Error} - If the story is already finished or not started.
*/
public async timeInDevelopment(): Promise<number> {
Expand All @@ -113,7 +127,7 @@ export default class Story extends ShortcutResource {
if (!this.startedAt) {
throw new Error('Story is not started')
}
return (new Date().getTime() - this.startedAt!.getTime()) / (1000 * 60 * 60 * 24)
return (new Date().getTime() - this.startedAt!.getTime()) / (1000 * 60 * 60)
}

public async comment(comment: string): Promise<StoryComment | void> {
Expand Down
49 changes: 0 additions & 49 deletions tests/iterations/iterations-service.test.js

This file was deleted.

Loading

0 comments on commit f2d2dc1

Please sign in to comment.