diff --git a/docs/assets/search.js b/docs/assets/search.js index 72079b3..c1c4f8b 100644 --- a/docs/assets/search.js +++ b/docs/assets/search.js @@ -1 +1 @@ -window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file +window.searchData = "data:application/octet-stream;base64,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"; \ No newline at end of file diff --git a/docs/classes/Client.html b/docs/classes/Client.html index fb969d0..086c607 100644 --- a/docs/classes/Client.html +++ b/docs/classes/Client.html @@ -1,4 +1,4 @@ -Client | shortcut-api

Class Client

Constructors

constructor +Client | shortcut-api

Class Client

Constructors

Properties

Accessors

Constructors

Properties

iterations: IterationsService
shortcutApiKey: undefined | string
workflows: WorkflowsService
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

Accessors

  • get headers(): ShortcutHeaders
  • Returns ShortcutHeaders

Generated using TypeDoc

\ No newline at end of file +

Constructors

Properties

iterations: IterationsService
shortcutApiKey: undefined | string
workflows: WorkflowsService
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

Accessors

  • get headers(): ShortcutHeaders
  • Returns ShortcutHeaders

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/Iteration.html b/docs/classes/Iteration.html index 95672fe..4052dab 100644 --- a/docs/classes/Iteration.html +++ b/docs/classes/Iteration.html @@ -1,4 +1,4 @@ -Iteration | shortcut-api

Class Iteration

Inherit Doc

Hierarchy

  • default
    • Iteration

Constructors

constructor +Iteration | shortcut-api

Class Iteration

Inherit Doc

Hierarchy

  • default
    • Iteration

Constructors

Properties

Constructors

Properties

appUrl: string
changedFields: string[] = []

Fields that have been changed, used to determine what to update

-
createFields: string[] = ...
createdAt: Date
endDate: string
entityType: string
followerIds: string[]
groupIds: string[]
groupMentionIds: string[]
id: number
labelIds: number[]
labels: Label[]
memberMentionIds: string[]
mentionIds: string[]
name: string
startDate: Date
stats: IterationStats
status: "unstarted" | "started" | "done"
updatedAt: Date
baseUrl: string = 'https://api.app.shortcut.com/api/v3/iterations'

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    +

Constructors

Properties

appUrl: string
changedFields: string[] = []

Fields that have been changed, used to determine what to update

+
createFields: string[] = ...
createdAt: Date
endDate: string
entityType: string
followerIds: string[]
groupIds: string[]
groupMentionIds: string[]
id: number
labelIds: number[]
labels: Label[]
memberMentionIds: string[]
mentionIds: string[]
name: string
startDate: Date
stats: IterationStats
status: "unstarted" | "started" | "done"
updatedAt: Date
baseUrl: string = 'https://api.app.shortcut.com/api/v3/iterations'

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    Returns Promise<Iteration>

    • A Promise that resolves with the newly created instance.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Delete the current instance of the resource.

    +
  • Delete the current instance of the resource.

    Returns Promise<void>

    • A Promise that resolves when the resource has been deleted.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. +

  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. Otherwise, it will be created using the fields createFields.

    -

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    +

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    Returns Promise<void>

    • A Promise that resolves when the resource has been updated.

    Throws

    • Throws an error if the HTTP request fails.
    -

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/IterationsService.html b/docs/classes/IterationsService.html index fdf2c32..da0650d 100644 --- a/docs/classes/IterationsService.html +++ b/docs/classes/IterationsService.html @@ -1,12 +1,11 @@ -IterationsService | shortcut-api

Class IterationsService

Hierarchy

Constructors

constructor +IterationsService | shortcut-api

Class IterationsService

Hierarchy

Constructors

  • Service classes are not intended to be instantiated directly. Instead, use the Client class to create instances of services.

    -

    Parameters

    • init: {
          headers: Record<string, string>;
      }
      • headers: Record<string, string>

    Returns IterationsService

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/iterations'
headers: Record<string, string>
instances: Record<string, Iteration> = {}
iterations: Record<number, Iteration> = {}

Methods

Generated using TypeDoc

\ No newline at end of file +

Parameters

  • init: {
        headers: Record<string, string>;
    }
    • headers: Record<string, string>

Returns IterationsService

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/iterations'
headers: Record<string, string>
instances: Record<string, Iteration> = {}
iterations: Record<number, Iteration> = {}

Methods

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/Member.html b/docs/classes/Member.html index 13273cd..0727578 100644 --- a/docs/classes/Member.html +++ b/docs/classes/Member.html @@ -1,4 +1,4 @@ -Member | shortcut-api

Class Member

Inherit Doc

Hierarchy

  • default
    • Member

Constructors

constructor +Member | shortcut-api

Class Member

Inherit Doc

Hierarchy

  • default
    • Member

Constructors

Properties

Constructors

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/members'
changedFields: string[] = []

Fields that have been changed, used to determine what to update

-
createFields: string[] = []

Fields that are used when creating a new resource

-
createdAt: string
disabled: boolean
entityType: string
groupIds: string[]
id: string
profile: object
role: string
state: "disabled" | "full" | "imported" | "partial"
updatedAt: string
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

The base URL for the API endpoint related to the resource

-

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    +

Constructors

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/members'
changedFields: string[] = []

Fields that have been changed, used to determine what to update

+
createFields: string[] = []

Fields that are used when creating a new resource

+
createdAt: string
disabled: boolean
entityType: string
groupIds: string[]
id: string
profile: object
role: string
state: "disabled" | "full" | "imported" | "partial"
updatedAt: string
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

The base URL for the API endpoint related to the resource

+

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    Returns Promise<Member>

    • A Promise that resolves with the newly created instance.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Delete the current instance of the resource.

    +
  • Delete the current instance of the resource.

    Returns Promise<void>

    • A Promise that resolves when the resource has been deleted.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. +

  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. Otherwise, it will be created using the fields createFields.

    -

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    +

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    Returns Promise<void>

    • A Promise that resolves when the resource has been updated.

    Throws

    • Throws an error if the HTTP request fails.
    -

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/MembersService.html b/docs/classes/MembersService.html index 3bc7cc8..66d74da 100644 --- a/docs/classes/MembersService.html +++ b/docs/classes/MembersService.html @@ -1,4 +1,4 @@ -MembersService | shortcut-api

Class MembersService

Hierarchy

Constructors

constructor +MembersService | shortcut-api

Class MembersService

Hierarchy

Constructors

Properties

Constructors

  • Service classes are not intended to be instantiated directly. Instead, use the Client class to create instances of services.

    -

    Parameters

    • init: {
          headers: Record<string, string>;
      }
      • headers: Record<string, string>

    Returns MembersService

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/members'
headers: Record<string, string>
instances: Record<string, Member> = {}
members: Record<number, Member> = {}

Methods

  • Parameters

    • ids: string[] | number[]

    Returns Promise<Member[]>

Generated using TypeDoc

\ No newline at end of file +

Parameters

  • init: {
        headers: Record<string, string>;
    }
    • headers: Record<string, string>

Returns MembersService

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/members'
headers: Record<string, string>
instances: Record<string, Member> = {}
members: Record<number, Member> = {}

Methods

  • Parameters

    • ids: string[] | number[]

    Returns Promise<Member[]>

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/StoriesService.html b/docs/classes/StoriesService.html index e7a3a71..84d93b2 100644 --- a/docs/classes/StoriesService.html +++ b/docs/classes/StoriesService.html @@ -1,4 +1,4 @@ -StoriesService | shortcut-api

Class StoriesService

Inherit Doc

Hierarchy

  • default<Story>
    • StoriesService

Constructors

constructor +StoriesService | shortcut-api

Class StoriesService

Inherit Doc

Hierarchy

  • default<Story>
    • StoriesService

Constructors

Properties

Constructors

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/stories'
headers: Record<string, string>
instances: Record<string, Story> = {}

Methods

  • Parameters

    • ids: string[] | number[]

    Returns Promise<Story[]>

Constructors

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/stories'
headers: Record<string, string>
instances: Record<string, Story> = {}

Methods

  • Parameters

    • ids: string[] | number[]

    Returns Promise<Story[]>

  • Search for stories using the Shortcut Syntax

    Parameters

    • query: string

    Returns Promise<Story[]>

    Example

    const client = new Client()
    const stories = client.stories.search('type:bug')

    Throws

    Error if the HTTP status code is 400 or greater

    -

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/Story.html b/docs/classes/Story.html index 52c46de..14228c1 100644 --- a/docs/classes/Story.html +++ b/docs/classes/Story.html @@ -2,7 +2,7 @@ -

Hierarchy

Constructors

Hierarchy

  • default
    • Story

Constructors

Properties

Accessors

Accessors

Constructors

Properties

appUrl: string
archived: boolean
blocked: boolean
blocker: boolean
branches: Branch[]
changedFields: string[] = []

Fields that have been changed, used to determine what to update

-
comments: StoryComment[]
commits: Commit[]
completed: boolean
completedAt: null | Date
completedAtOverride: null | Date
createFields: string[] = []

Fields that are used when creating a new resource

-
createdAt: Date
customFields: StoryCustomField[]
deadline: null | Date
description: string
entityType: string
epicId: null | number
estimate: null | number
externalId: null | string
externalLinks: string[]
files: UploadedFile[]
followerIds: string[]
groupId: null | string
groupMentionIds: string[]
id: number
iterationId: null | number
labelIds: number[]
labels: LabelSlim[]
leadTime: number
linkedFiles: LinkedFile[]
memberMentionIds: string[]
mentionIds: string[]
movedAt: null | Date
name: string
ownerIds: string[]
position: number
previousIterationIds: number[]
projectId: null | number
requestedById: string
started: boolean
startedAt: null | Date
startedAtOverride: null | Date
stats: StoryStats
storyLinks: TypedStoryLink[]
storyTemplateId: null | string
storyType: string
syncedItem: SyncedItem
tasks: Task[]
unresolvedBlockerComments: number[]
updatedAt: null | Date
workflowId: number
workflowStateId: number
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

The base URL for the API endpoint related to the resource

-

Accessors

  • get team(): Promise<Team>
  • Get the team assigned to the story, labelled as "Group" in the Shortcut API

    -

    Returns Promise<Team>

  • get workflow(): Promise<WorkflowStateInterface>
  • Returns Promise<WorkflowStateInterface>

Methods

  • Parameters

    • comment: string

    Returns Promise<void | StoryComment>

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    +

Constructors

Properties

appUrl: string
archived: boolean
blocked: boolean
blocker: boolean
branches: Branch[]
changedFields: string[] = []

Fields that have been changed, used to determine what to update

+
comments: StoryComment[]
commits: Commit[]
completed: boolean
completedAt: null | Date
completedAtOverride: null | Date
createFields: string[] = []

Fields that are used when creating a new resource

+
createdAt: Date
customFields: StoryCustomField[]
deadline: null | Date
description: string
entityType: string
epicId: null | number
estimate: null | number
externalId: null | string
externalLinks: string[]
files: UploadedFile[]
followerIds: string[]
groupId: null | string
groupMentionIds: string[]
id: number
iterationId: null | number
labelIds: number[]
labels: LabelSlim[]
leadTime: number
linkedFiles: LinkedFile[]
memberMentionIds: string[]
mentionIds: string[]
movedAt: null | Date
name: string
ownerIds: string[]
position: number
previousIterationIds: number[]
projectId: null | number
requestedById: string
started: boolean
startedAt: null | Date
startedAtOverride: null | Date
stats: StoryStats
storyLinks: TypedStoryLink[]
storyTemplateId: null | string
storyType: string
syncedItem: SyncedItem
tasks: Task[]
unresolvedBlockerComments: number[]
updatedAt: null | Date
workflowId: number
workflowStateId: number
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

The base URL for the API endpoint related to the resource

+

Accessors

Methods

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/Team.html b/docs/classes/Team.html index 58b3003..192ba8f 100644 --- a/docs/classes/Team.html +++ b/docs/classes/Team.html @@ -1,4 +1,4 @@ -Team | shortcut-api

Inherit Doc

Hierarchy

  • default
    • Team

Constructors

constructor +Team | shortcut-api

Inherit Doc

Hierarchy

  • default
    • Team

Constructors

Properties

Constructors

  • Parameters

    • Optional init: object

    Returns Team

Properties

appUrl: string
archived: boolean
changedFields: string[] = []

Fields that have been changed, used to determine what to update

-
color: string
colorKey: string
createFields: string[] = ...
description: string
displayIcon: string
entityType: string
id: string
memberIds: number[]
mentionName: string
name: string
numEpicsStarted: number
numStoriesStarted: number
workflowIds: number[]
baseUrl: string = 'https://api.app.shortcut.com/api/v3/groups'

Accessors

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    +

Constructors

  • Parameters

    • Optional init: object

    Returns Team

Properties

appUrl: string
archived: boolean
changedFields: string[] = []

Fields that have been changed, used to determine what to update

+
color: string
colorKey: string
createFields: string[] = ...
description: string
displayIcon: string
entityType: string
id: string
memberIds: number[]
mentionName: string
name: string
numEpicsStarted: number
numStoriesStarted: number
workflowIds: number[]
baseUrl: string = 'https://api.app.shortcut.com/api/v3/groups'

Accessors

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    Returns Promise<Team>

    • A Promise that resolves with the newly created instance.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Delete the current instance of the resource.

    +
  • Delete the current instance of the resource.

    Returns Promise<void>

    • A Promise that resolves when the resource has been deleted.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. +

  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. Otherwise, it will be created using the fields createFields.

    -

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    +

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    Returns Promise<void>

    • A Promise that resolves when the resource has been updated.

    Throws

    • Throws an error if the HTTP request fails.
    -

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/TeamService.html b/docs/classes/TeamService.html index 622bd72..4006cfc 100644 --- a/docs/classes/TeamService.html +++ b/docs/classes/TeamService.html @@ -1,4 +1,4 @@ -TeamService | shortcut-api

Class TeamService

Hierarchy

  • default<Team>
    • TeamService

Constructors

constructor +TeamService | shortcut-api

Class TeamService

Hierarchy

  • default<Team>
    • TeamService

Constructors

Properties

baseUrl headers instances @@ -10,4 +10,4 @@ getMany list

Constructors

  • Service classes are not intended to be instantiated directly. Instead, use the Client class to create instances of services.

    -

    Parameters

    • init: {
          headers: Record<string, string>;
      }
      • headers: Record<string, string>

    Returns TeamService

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/groups'
headers: Record<string, string>
instances: Record<string, Team> = {}
teams: Record<number, Team> = {}

Methods

  • Parameters

    • id: string | number

    Returns Promise<Team>

  • Parameters

    • ids: string[] | number[]

    Returns Promise<Team[]>

Generated using TypeDoc

\ No newline at end of file +

Parameters

  • init: {
        headers: Record<string, string>;
    }
    • headers: Record<string, string>

Returns TeamService

Properties

baseUrl: string = 'https://api.app.shortcut.com/api/v3/groups'
headers: Record<string, string>
instances: Record<string, Team> = {}
teams: Record<number, Team> = {}

Methods

  • Parameters

    • id: string | number

    Returns Promise<Team>

  • Parameters

    • ids: string[] | number[]

    Returns Promise<Team[]>

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/Workflow.html b/docs/classes/Workflow.html index ea10566..1c4fdc6 100644 --- a/docs/classes/Workflow.html +++ b/docs/classes/Workflow.html @@ -1,4 +1,4 @@ -Workflow | shortcut-api

Class Workflow

Inherit Doc

Hierarchy

  • default
    • Workflow

Constructors

constructor +Workflow | shortcut-api

Class Workflow

Inherit Doc

Hierarchy

  • default
    • Workflow

Constructors

Properties

Constructors

Properties

autoAssignOwner: boolean
changedFields: string[] = []

Fields that have been changed, used to determine what to update

-
createFields: string[] = []

Fields that are used when creating a new resource

-
createdAt: Date
defaultStateId: number
description: string
entityType: string
id: number
name: string
projectIds: number[]
states: WorkflowStateInterface[]
teamId: number
updatedAt: Date
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

The base URL for the API endpoint related to the resource

-

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    +

Constructors

Properties

autoAssignOwner: boolean
changedFields: string[] = []

Fields that have been changed, used to determine what to update

+
createFields: string[] = []

Fields that are used when creating a new resource

+
createdAt: Date
defaultStateId: number
description: string
entityType: string
id: number
name: string
projectIds: number[]
states: WorkflowStateInterface[]
teamId: number
updatedAt: Date
baseUrl: string = 'https://api.app.shortcut.com/api/v3'

The base URL for the API endpoint related to the resource

+

Methods

  • Create a new instance of the resource, using the current object's properties. Use the xCreateData interface to determine which fields are available for creation.

    Returns Promise<Workflow>

    • A Promise that resolves with the newly created instance.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Delete the current instance of the resource.

    +
  • Delete the current instance of the resource.

    Returns Promise<void>

    • A Promise that resolves when the resource has been deleted.

    Throws

    • Throws an error if the HTTP request fails.
    -
  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. +

  • Save the current instance of the resource. If the resource already exists (has an ID), it will be updated. Otherwise, it will be created using the fields createFields.

    -

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    +

    Returns Promise<void>

  • Update the current instance of the resource with the changed fields.

    Returns Promise<void>

    • A Promise that resolves when the resource has been updated.

    Throws

    • Throws an error if the HTTP request fails.
    -

Generated using TypeDoc

\ No newline at end of file +

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/classes/WorkflowsService.html b/docs/classes/WorkflowsService.html index dd78b45..7ea20c6 100644 --- a/docs/classes/WorkflowsService.html +++ b/docs/classes/WorkflowsService.html @@ -1,4 +1,4 @@ -WorkflowsService | shortcut-api

Class WorkflowsService

Hierarchy

Constructors

constructor +WorkflowsService | shortcut-api

Class WorkflowsService

Hierarchy

Constructors

Properties

Constructors

  • Service classes are not intended to be instantiated directly. Instead, use the Client class to create instances of services.

    -

    Parameters

    • init: {
          headers: Record<string, string>;
      }
      • headers: Record<string, string>

    Returns WorkflowsService

Properties

baseUrl: string = ''
factory: ((data) => Workflow)

Type declaration

headers: Record<string, string>
instances: Record<string, Workflow> = {}

Methods

  • Parameters

    • workflows: WorkflowInterface[]

    Returns WorkflowStateInterface[]

Generated using TypeDoc

\ No newline at end of file +

Parameters

  • init: {
        headers: Record<string, string>;
    }
    • headers: Record<string, string>

Returns WorkflowsService

Properties

baseUrl: string = ''
factory: ((data) => Workflow)

Type declaration

headers: Record<string, string>
instances: Record<string, Workflow> = {}

Methods

  • Parameters

    • workflows: WorkflowInterface[]

    Returns WorkflowStateInterface[]

Generated using TypeDoc

\ No newline at end of file diff --git a/docs/coverage.svg b/docs/coverage.svg index f948cce..3d573e1 100644 --- a/docs/coverage.svg +++ b/docs/coverage.svg @@ -11,7 +11,7 @@ Docs Docs - 19% - 19% + 20% + 20% \ No newline at end of file diff --git a/src/epics/contracts/epic-api-data.ts b/src/epics/contracts/epic-api-data.ts new file mode 100644 index 0000000..d6a4c28 --- /dev/null +++ b/src/epics/contracts/epic-api-data.ts @@ -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 +} \ No newline at end of file diff --git a/src/epics/contracts/epic-interface.ts b/src/epics/contracts/epic-interface.ts new file mode 100644 index 0000000..bcea76a --- /dev/null +++ b/src/epics/contracts/epic-interface.ts @@ -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 +} \ No newline at end of file diff --git a/src/epics/epic.ts b/src/epics/epic.ts new file mode 100644 index 0000000..5daebd9 --- /dev/null +++ b/src/epics/epic.ts @@ -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} + */ + get teams(): Promise { + const service = new TeamService({headers: getHeaders()}) + return service.getMany(this.groupIds) + } + + /** + * Get the members following the epic + * @returns {Promise} + */ + get followers(): Promise { + 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 +} \ No newline at end of file diff --git a/src/epics/epics-service.ts b/src/epics/epics-service.ts new file mode 100644 index 0000000..9fa50cc --- /dev/null +++ b/src/epics/epics-service.ts @@ -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 { + public baseUrl = 'https://api.app.shortcut.com/api/v3/epics' + protected factory = (data: object) => new Epic(data) + public static epics: Record = {} + +} \ No newline at end of file diff --git a/src/iterations/iterations-service.ts b/src/iterations/iterations-service.ts index 43c0d81..3df4702 100644 --- a/src/iterations/iterations-service.ts +++ b/src/iterations/iterations-service.ts @@ -10,13 +10,4 @@ export default class IterationsService extends BaseService { public baseUrl = 'https://api.app.shortcut.com/api/v3/iterations' protected factory = (data: object) => new Iteration(data) public static iterations: Record = {} - - public async create(iteration: CreateIterationData): Promise { - 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) - } } diff --git a/src/stories/story.ts b/src/stories/story.ts index a7c4141..a6c59a8 100644 --- a/src/stories/story.ts +++ b/src/stories/story.ts @@ -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' @@ -73,6 +75,18 @@ export default class Story extends ShortcutResource { return service.getMany(this.ownerIds) } + /** + * Get the epic of the story + * @returns {Promise} + */ + get epic(): Promise { + 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 { const url = `${Story.baseUrl}/stories/${this.id}/history` const response = await axios.get(url, {headers: getHeaders()}).catch((error) => { @@ -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} - The cycle time in days. + * @returns {Promise} - The cycle time in hours. * @throws {Error} - If the story is not completed or has not been started. */ public async cycleTime(): Promise { @@ -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} - The time in development in days. + * @returns {Promise} - The time in development in hours. * @throws {Error} - If the story is already finished or not started. */ public async timeInDevelopment(): Promise { @@ -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 { diff --git a/tests/iterations/iterations-service.test.js b/tests/iterations/iterations-service.test.js deleted file mode 100644 index 1694c5c..0000000 --- a/tests/iterations/iterations-service.test.js +++ /dev/null @@ -1,49 +0,0 @@ -import axios from 'axios' -import IterationsService from '../../src/iterations/iterations-service' -import Iteration from '../../src/iterations/iteration' - - -jest.mock('axios') - -describe('IterationsService', () => { - let iterationsService - - beforeEach(() => { - iterationsService = new IterationsService({headers: {'Content-Type': 'application/json', 'Shortcut-Token': 'token'}}) - jest.clearAllMocks() // Clears the mock call history before each test - }) - - it('should create an iteration successfully', async () => { - const mockIterationData = { - name: 'Test Iteration', - startDate: '2023-01-01', - endDate: '2023-01-07', - labels: [] - } - const mockResponse = {status: 200, data: {...mockIterationData, id: 123}} - axios.post.mockResolvedValue(mockResponse) - - const result = await iterationsService.create(mockIterationData) - - expect(axios.post).toHaveBeenCalledWith(iterationsService.baseUrl, mockIterationData, {headers: iterationsService.headers}) - expect(result).toBeInstanceOf(Iteration) - expect(result.id).toBe(123) - }) - - it('should throw an error when the response status is 400 or above', async () => { - const mockIterationData = { - name: 'Test Iteration', - startDate: '2023-01-01', - endDate: '2023-01-07', - labels: [] - } - const mockErrorResponse = {status: 400} - axios.post.mockResolvedValue(mockErrorResponse) - - await expect(iterationsService.create(mockIterationData)).rejects.toThrow('HTTP error 400') - - expect(axios.post).toHaveBeenCalledWith(iterationsService.baseUrl, mockIterationData, {headers: iterationsService.headers}) - }) - - // Additional tests can be written to cover other methods and edge cases -}) diff --git a/tests/story/story.test.js b/tests/story/story.test.js index 11184be..5f91a1b 100644 --- a/tests/story/story.test.js +++ b/tests/story/story.test.js @@ -2,6 +2,11 @@ import axios from 'axios' import Story from '../../src/stories/story' import {convertApiFields} from '../../src/utils/convert-fields' import {getHeaders} from '../../src/utils/headers' +import Iteration from '../../src/iterations/iteration' +import WorkflowService from '../../src/workflows/workflows-service' +import TeamService from '../../src/teams/team-service' +import MembersService from '../../src/members/members-service' +import EpicsService from '../../src/epics/epics-service' jest.mock('axios', () => ({ @@ -10,19 +15,19 @@ jest.mock('axios', () => ({ put: jest.fn(), delete: jest.fn() })) -// Mock WorkflowService, IterationsService, getHeaders, convertApiFields as necessary describe('Story', () => { process.env.SHORTCUT_API_KEY = 'token' beforeEach(() => { - // Reset mocks and any setup before each test axios.post.mockClear() }) describe('workflow getter', () => { it('should return workflow state by ID', () => { - // Mock WorkflowService.getWorkflowState to return a specific workflow state - // Test the workflow getter + const story = new Story({workflowStateId: 1}) + jest.spyOn(WorkflowService, 'getWorkflowState').mockReturnValue({id: 1, name: 'Unstarted'}) + expect(story.workflow).toEqual({id: 1, name: 'Unstarted'}) + expect(WorkflowService.getWorkflowState).toHaveBeenCalledWith(1) }) }) @@ -33,8 +38,127 @@ describe('Story', () => { }) it('returns an iteration if story has an iteration ID', async () => { - // Mock IterationsService.get to return a specific iteration - // Test the iteration getter for a valid case + const iterationData = {id: 1, name: 'Test iteration'} + const iteration = new Iteration(iterationData) + axios.get.mockResolvedValue({data: iterationData}) + const story = new Story({iterationId: 1}) + const result = await story.iteration + expect(result).toEqual(iteration) + }); + }) + + describe('team getter', () => { + it('should throw an error if no team ID is set', () => { + const story = new Story({groupId: null}) + expect(() => story.team).toThrow('Story does not have a team') + }) + + it('should return the team object', () => { + const teamData = {id: 1, name: 'Test team'} + jest.spyOn(TeamService.prototype, 'get').mockReturnValue(teamData) + const story = new Story({groupId: 1}) + expect(story.team).toEqual(teamData) + expect(TeamService.prototype.get).toHaveBeenCalledWith(1) + }) + }) + + describe('owner getter', () => { + it('should return an array of members', () => { + const story = new Story({ownerIds: [1, 2]}) + const users = [{id: 1, name: 'Test user 1'}, { + id: 2, + name: 'Test user 2' + }] + jest.spyOn(MembersService.prototype, 'getMany').mockReturnValue(users) + expect(story.owners).toEqual([{id: 1, name: 'Test user 1'}, {id: 2, name: 'Test user 2'}]) + }) + }) + + describe('epic getter', () => { + it('should throw an error if no epic ID is set', () => { + const story = new Story({epicId: null}) + expect(() => story.epic).toThrow('Story does not have an epic') + }) + it('should return the epic object', () => { + const epicData = {id: 1, name: 'Test epic'} + jest.spyOn(EpicsService.prototype, 'get').mockReturnValue(epicData) + const story = new Story({epicId: 1}) + expect(story.epic).toEqual(epicData) + expect(EpicsService.prototype.get).toHaveBeenCalledWith(1) + }) + }) + + describe('history method', () => { + it('should throw an error if request fails', () => { + const story = new Story({id: 1}) + axios.get.mockRejectedValue(new Error('Network error')) + expect(story.history()).rejects.toThrow('Error fetching history: Error: Network error') + }) + + it('should return the story history', () => { + const story = new Story({id: 1}) + const history = [{id: 1, name: 'Test history'}] + axios.get.mockResolvedValue({data: history}) + expect(story.history()).resolves.toEqual(history) + expect(axios.get).toHaveBeenCalledWith(`${Story.baseUrl}/stories/${story.id}/history`, {headers: getHeaders()}) + }) + }) + + describe('cycleTime method', () => { + it('should throw an error if start date is not set', () => { + const story = new Story({id: 1, startedAt: null, completedAt: null}) + expect(story.cycleTime()).rejects.toThrow('Story does not have a cycle time') + }) + + it('should throw an error if completed date is not set', () => { + const story = new Story({id: 1, startedAt: new Date('2021-01-01')}) + expect(() => story.cycleTime()).rejects.toThrow('Story does not have a cycle time') + }) + + it('should return the cycle time', () => { + const startedAt = new Date('2021-01-01') + const completedAt = new Date('2021-01-05') + const story = new Story({id: 1, startedAt: startedAt, completedAt: completedAt}) + expect(story.cycleTime()).resolves.toEqual(4 * 24) + }) + }) + + describe('timeInDevelopment method', () => { + const mockedDate = new Date('2022-03-28') + const realDate = Date + + beforeEach(() => { + global.Date = jest.fn(() => mockedDate) + global.Date.now = realDate.now + }) + + afterEach(() => { + global.Date = realDate + }) + + + it('should throw an error if workflow state is finished', () => { + jest.spyOn(WorkflowService, 'getWorkflowState').mockReturnValue({id: 1, type: 'Finished'}) + const story = new Story({id: 1, workflowStateId: 1}) + expect(story.timeInDevelopment()).rejects.toThrow('Story is already finished') + }) + + it('should throw an error if story does not have a started date', () => { + jest.spyOn(WorkflowService, 'getWorkflowState').mockReturnValue({id: 1, type: 'Started'}) + const story = new Story({id: 1, startedAt: null}) + expect(story.timeInDevelopment()).rejects.toThrow('Story is not started') + }) + + it('should return the time in development', async () => { + const mockWorkflow = {type: 'SomeWorkflowType'} + const mockStartedAt = new Date('2021-01-01') + + jest.spyOn(Story.prototype, 'workflow', 'get').mockResolvedValue(mockWorkflow) + + const story = new Story({id: 1, workflowStateId: 1, startedAt: null}) + story.startedAt = mockStartedAt + const expectedTimeInDevelopment = (mockedDate.getTime() - mockStartedAt.getTime()) / (1000 * 60 * 60) + await expect(story.timeInDevelopment()).resolves.toEqual(expectedTimeInDevelopment) }) }) @@ -43,9 +167,8 @@ describe('Story', () => { const commentData = {text: 'Test comment'} const expectedResponse = {data: commentData} axios.post.mockResolvedValue(expectedResponse) - // Mock convertApiFields to return a camelCase version of the data - const story = new Story({id: 1}) // Adjust initial data as needed + const story = new Story({id: 1}) const result = await story.comment('Test comment') expect(result).toEqual(convertApiFields(commentData))