Created
February 1, 2024 21:01
-
-
Save ryan-nauman/737ffd49215ad2615ed675f11fa32c80 to your computer and use it in GitHub Desktop.
script kit jira search
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Name: Jira Search | |
import '@johnlindquist/kit'; | |
import { Buffer } from 'buffer'; | |
function adfConvert(node) { | |
switch (node.type) { | |
case 'doc': | |
return node.content.map((node) => adfConvert(node)).join('\n\n'); | |
case 'text': | |
return `${convertMarks(node)}`; | |
case 'paragraph': | |
return node.content.map((node) => adfConvert(node)).join(''); | |
case 'heading': | |
return `${'#'.repeat(node.attrs.level)} ${node.content | |
.map((node) => adfConvert(node)) | |
.join('')}`; | |
case 'hardBreak': | |
return '\n'; | |
case 'inlineCard': | |
case 'blockCard': | |
case 'embedCard': | |
return `[${node.attrs.url}](${node.attrs.url})`; | |
case 'blockquote': | |
return `> ${node.content.map((node) => adfConvert(node)).join('\n> ')}`; | |
case 'bulletList': | |
case 'orderedList': | |
return `${node.content | |
.map((subNode) => { | |
const converted = adfConvert.call(node, subNode); | |
if (node.type === 'orderedList') { | |
if (!node.attrs) { | |
node.attrs = { | |
order: 1, | |
}; | |
} | |
node.attrs.order += 1; | |
} | |
return converted; | |
}) | |
.join('\n')}`; | |
case 'listItem': { | |
const order = this.attrs ? this.attrs.order || 1 : 1; | |
const symbol = this.type === 'bulletList' ? '*' : `${order}.`; | |
return ` ${symbol} ${node.content | |
.map((node) => adfConvert(node).trimEnd()) | |
.join(` `)}`; | |
} | |
case 'codeBlock': { | |
const language = node.attrs ? ` ${node.attrs.language}` : ''; | |
return `\`\`\`${language}\n${node.content | |
.map((node) => adfConvert(node)) | |
.join('\n')}\n\`\`\``; | |
} | |
case 'rule': | |
return '\n\n---\n'; | |
case 'emoji': | |
return node.attrs.shortName; | |
case 'table': | |
return node.content.map((node) => adfConvert(node)).join(''); | |
case 'tableRow': { | |
let output = '|'; | |
let thCount = 0; | |
output += node.content | |
.map((subNode) => { | |
thCount += subNode.type === 'tableHeader' ? 1 : 0; | |
return adfConvert(subNode); | |
}) | |
.join(''); | |
output += thCount ? `\n${'|:-:'.repeat(thCount)}|\n` : '\n'; | |
return output; | |
} | |
case 'tableHeader': | |
return `${node.content.map((node) => adfConvert(node)).join('')}|`; | |
case 'tableCell': | |
return `${node.content.map((node) => adfConvert(node)).join('')}|`; | |
default: | |
return ''; | |
} | |
} | |
function convertMarks(node) { | |
if (!node.hasOwnProperty('marks') || !Array.isArray(node.marks)) { | |
return node.text; | |
} | |
return node.marks.reduce((converted, mark) => { | |
switch (mark.type) { | |
case 'code': | |
converted = `\`${converted}\``; | |
break; | |
case 'em': | |
converted = `_${converted}_`; | |
break; | |
case 'link': | |
converted = `[${converted}](${mark.attrs.href})`; | |
break; | |
case 'strike': | |
converted = `~${converted}~`; | |
break; | |
case 'strong': | |
converted = `**${converted}**`; | |
break; | |
default: | |
break; | |
} | |
return converted; | |
}, node.text); | |
} | |
function encodeToBase64Node(input: string): string { | |
return Buffer.from(input).toString('base64'); | |
} | |
const ATLASSIAN_EMAIL = await env('ATLASSIAN_EMAIL', async () => { | |
return await arg('Enter your jira email address'); | |
}); | |
const ATLASSIAN_TOKEN = await env('ATLASSIAN_TOKEN', async () => { | |
return await arg('Enter your jira api token'); | |
}); | |
const BROWSE_ISSUE_URL_PREFIX = 'https://studio-x.atlassian.net/browse/'; | |
const SEARCH_API_PREFIX = 'https://studio-x.atlassian.net/rest/api/3/search'; | |
const searchMethod = await arg('How would you like to search jira?', [ | |
{ name: 'Text search', value: 'text' }, | |
{ name: 'Key (e.g. VOLT-1)', value: 'key' }, | |
{ name: 'JQL (e.g. project = VOLT)', value: 'jql' }, | |
]); | |
const searchQuery = await arg('Enter the search value'); | |
let jql; | |
switch (searchMethod) { | |
case 'text': | |
jql = `text ~ "${searchQuery}"`; | |
break; | |
case 'key': | |
jql = `key = ${searchQuery}`; | |
break; | |
case 'jql': | |
jql = searchQuery; | |
break; | |
} | |
let { data } = await post<Jira>( | |
`${SEARCH_API_PREFIX}`, | |
{ | |
jql, | |
maxResults: 10, | |
fields: ['summary', 'assignee', 'creator', 'description'], | |
fieldsByKeys: false, | |
}, | |
{ | |
headers: { | |
Authorization: `Basic ${encodeToBase64Node( | |
`${ATLASSIAN_EMAIL}:${ATLASSIAN_TOKEN}`, | |
)}`, | |
}, | |
}, | |
); | |
let issueKey = await arg( | |
`Select Issue:`, | |
data.issues.map(({ fields, key }) => { | |
return { | |
name: key, | |
description: fields.summary, | |
value: key, | |
preview: () => { | |
const adf = adfConvert(fields?.description); | |
return md(`# ${key} ${fields?.summary} | |
🧑🍳 ${fields?.assignee?.displayName || 'Unassigned'} | |
📖 ${fields?.creator?.displayName || 'Unknown'} | |
#### Description | |
${adf} | |
`); | |
}, | |
}; | |
}), | |
); | |
await $`open ${BROWSE_ISSUE_URL_PREFIX}${issueKey}`; | |
export interface Jira { | |
expand: string; | |
issues: Issue[]; | |
maxResults: number; | |
startAt: number; | |
total: number; | |
} | |
export interface Issue { | |
expand: string; | |
fields: IssueFields; | |
id: string; | |
key: string; | |
self: string; | |
} | |
export interface IssueFields { | |
aggregateprogress: Progress; | |
aggregatetimeestimate: null; | |
aggregatetimeoriginalestimate: null; | |
aggregatetimespent: null; | |
assignee: Creator | null; | |
components: Priority[]; | |
created: string; | |
creator: Creator; | |
customfield_10100: string; | |
customfield_10101: null | string; | |
customfield_10105: null; | |
customfield_10106: null; | |
customfield_10200: string; | |
customfield_10201: null; | |
customfield_10202: null; | |
customfield_10203: null; | |
customfield_10204: null; | |
customfield_10205: null; | |
customfield_10206: null; | |
customfield_10207: null; | |
customfield_10208: null; | |
customfield_10209: null; | |
customfield_10210: null; | |
customfield_10211: null; | |
customfield_10212: null; | |
customfield_10213: null; | |
customfield_10214: null; | |
customfield_10215: Customfield10215; | |
customfield_10216: null | string; | |
customfield_10217: null | string; | |
customfield_10220: Customfield10107_Class[] | null; | |
customfield_10222: null; | |
customfield_10223: null; | |
customfield_10224: Customfield10224 | null; | |
customfield_10225: number | null; | |
customfield_10226: null; | |
customfield_10227: null; | |
customfield_10228: null; | |
customfield_10229: null; | |
customfield_10230: null; | |
customfield_10231: any[]; | |
customfield_10232: null; | |
customfield_10235: null; | |
customfield_10236: null; | |
customfield_10237: null; | |
customfield_10238: null; | |
customfield_10239: null; | |
customfield_10240: null; | |
customfield_10241: null; | |
customfield_10242: null; | |
customfield_10243: null; | |
customfield_10244: null; | |
customfield_10245: null; | |
customfield_10246: null; | |
customfield_10247: null; | |
customfield_10248: null; | |
customfield_10249: null; | |
customfield_10250: null; | |
customfield_10251: null; | |
customfield_10252: null; | |
customfield_10253: null; | |
customfield_10254: null; | |
customfield_10255: null; | |
customfield_10256: any[]; | |
customfield_10257: null; | |
customfield_10258: Customfield10258_Class | null; | |
customfield_10259: Customfield10258_Class | null; | |
customfield_10260: Customfield10258_Class | null; | |
customfield_10261: Customfield10258_Class | null; | |
customfield_10262: null; | |
customfield_10263: null; | |
customfield_10264: null; | |
customfield_10265: null; | |
customfield_10266: null; | |
customfield_10267: null; | |
customfield_10268: null; | |
customfield_10269: null; | |
customfield_10271: null; | |
customfield_10272: null; | |
customfield_10273: null; | |
customfield_10274: null; | |
customfield_10275: null; | |
customfield_10276: null; | |
customfield_10277: null; | |
customfield_10278: null; | |
customfield_10279: null; | |
customfield_10280: null; | |
customfield_10281: null; | |
customfield_10282: null; | |
customfield_10283: null; | |
customfield_10284: null; | |
customfield_10285: null; | |
customfield_10286: null; | |
customfield_10287: null; | |
customfield_10288: null; | |
customfield_10289: null; | |
customfield_10290: null; | |
customfield_10291: null; | |
customfield_10292: null; | |
customfield_10293: null; | |
customfield_10294: null; | |
customfield_10296: Creator[] | null; | |
customfield_10297: null; | |
customfield_10298: null; | |
customfield_10299: null; | |
customfield_10300: null; | |
customfield_10301: null; | |
customfield_10303: null; | |
customfield_10304: null; | |
customfield_10305: null; | |
customfield_10306: null; | |
customfield_10308: null; | |
customfield_10310: null; | |
customfield_10311: null; | |
customfield_10312: null; | |
customfield_10313: null; | |
customfield_10314: null; | |
customfield_10315: null; | |
customfield_10316: null; | |
customfield_10317: null; | |
customfield_10318: null; | |
customfield_10319: null; | |
customfield_10320: null; | |
customfield_10321: null; | |
customfield_10347: null; | |
customfield_10348: null; | |
customfield_10349: null; | |
customfield_10350: null; | |
customfield_10351: null; | |
customfield_10352: null; | |
customfield_10353: null; | |
customfield_10354: null; | |
customfield_10355: null; | |
customfield_10356: null; | |
customfield_10362: null; | |
customfield_10363: null; | |
customfield_10364: null; | |
customfield_10365: null; | |
customfield_10366: Customfield10107_Class | null; | |
customfield_10370: null; | |
customfield_10371: null; | |
customfield_10372: null; | |
customfield_10373: null; | |
customfield_10374: null; | |
customfield_10375: null; | |
customfield_10376: null; | |
customfield_10377: null; | |
customfield_10378: null; | |
customfield_10379: null; | |
customfield_10380: null; | |
customfield_10381: null; | |
customfield_10382: null; | |
customfield_10383: null; | |
customfield_10434: Customfield10258_Class | null; | |
customfield_10436: null; | |
customfield_10465: null; | |
customfield_10466: null; | |
description: DescriptionClass; | |
duedate: null; | |
environment: Environment | null; | |
fixVersions: any[]; | |
issuelinks: Issuelink[]; | |
issuetype: Issuetype; | |
labels: string[]; | |
lastViewed: null | string; | |
priority: Priority; | |
progress: Progress; | |
project: Project; | |
reporter: Creator; | |
resolution: Priority | null; | |
resolutiondate: null | string; | |
security: null; | |
status: Status; | |
statuscategorychangedate: string; | |
subtasks: Parent[]; | |
summary: string; | |
timeestimate: null; | |
timeoriginalestimate: null; | |
timespent: null; | |
updated: string; | |
versions: any[]; | |
votes: Votes; | |
watches: Watches; | |
workratio: number; | |
customfield_10107?: Customfield10107_Class; | |
parent?: Parent; | |
} | |
export interface Progress { | |
progress: number; | |
total: number; | |
} | |
export interface Creator { | |
accountId: string; | |
accountType: AccountType; | |
active: boolean; | |
avatarUrls: AvatarUrls; | |
displayName: string; | |
self: string; | |
timeZone: TimeZone; | |
emailAddress?: EmailAddress; | |
} | |
export enum AccountType { | |
App = 'app', | |
Atlassian = 'atlassian', | |
} | |
export interface AvatarUrls { | |
'16x16': string; | |
'24x24': string; | |
'32x32': string; | |
'48x48': string; | |
} | |
export enum EmailAddress { | |
RyanNaumanThinkonwardCOM = '[email protected]', | |
} | |
export enum TimeZone { | |
AmericaChicago = 'America/Chicago', | |
AmericaNewYork = 'America/New_York', | |
CanadaPacific = 'Canada/Pacific', | |
USPacific = 'US/Pacific', | |
} | |
export interface Priority { | |
id: string; | |
name: PriorityName; | |
self: string; | |
description?: DescriptionEnum; | |
iconUrl?: string; | |
} | |
export enum DescriptionEnum { | |
GreenHopperManagedResolution = 'GreenHopper Managed Resolution', | |
UsedToTagIssuesAsBackend = 'used to tag issues as backend', | |
UsedToTagStoriesAsFrontend = 'used to tag stories as frontend ', | |
} | |
export enum PriorityName { | |
Be = 'BE', | |
Critical = 'Critical', | |
Done = 'Done', | |
Fe = 'FE', | |
High = 'High', | |
Low = 'Low', | |
Medium = 'Medium', | |
Voltron = 'voltron', | |
} | |
export interface Customfield10107_Class { | |
id: string; | |
self: string; | |
value: Value; | |
} | |
export enum Value { | |
BugVoltron = 'Bug - Voltron', | |
Impediment = 'Impediment', | |
ToEstimate = 'TO ESTIMATE', | |
UserStoryVoltron = 'User Story - Voltron', | |
} | |
export interface Customfield10215 { | |
hasEpicLinkFieldDependency: boolean; | |
nonEditableReason: NonEditableReason; | |
showField: boolean; | |
} | |
export interface NonEditableReason { | |
message: Message; | |
reason: Reason; | |
} | |
export enum Message { | |
ToSetAnEpicAsTheParentUseTheEpicLinkInstead = 'To set an epic as the parent, use the epic link instead', | |
ToSetTheParentForASubTaskUseTheSubTaskLink = 'To set the parent for a sub-task, use the sub-task link.', | |
} | |
export enum Reason { | |
EpicLinkShouldBeUsed = 'EPIC_LINK_SHOULD_BE_USED', | |
SubtaskLinkShouldBeUsed = 'SUBTASK_LINK_SHOULD_BE_USED', | |
} | |
export enum Customfield10224 { | |
Checklist12 = 'Checklist: 1/2', | |
Empty = '', | |
} | |
export interface Customfield10258_Class { | |
errorMessage: ErrorMessage; | |
i18nErrorMessage: I18NErrorMessage; | |
} | |
export enum ErrorMessage { | |
TheServiceProjectYouAreTryingToViewDoesNotExist = 'The service project you are trying to view does not exist.', | |
} | |
export interface I18NErrorMessage { | |
i18nKey: I18NKey; | |
parameters: any[]; | |
} | |
export enum I18NKey { | |
SDJsmAgentServicedeskErrorServicedeskMissing = 'sd.jsm.agent.servicedesk.error.servicedesk.missing', | |
} | |
export interface DescriptionClass { | |
content: DescriptionContent[]; | |
type: DescriptionType; | |
version: number; | |
} | |
export interface DescriptionContent { | |
content?: PurpleContent[]; | |
type: IndigoType; | |
attrs?: PurpleAttrs; | |
} | |
export interface PurpleAttrs { | |
layout?: Layout; | |
order?: number; | |
level?: number; | |
isNumberColumnEnabled?: boolean; | |
localId?: string; | |
language?: string; | |
width?: number; | |
} | |
export enum Layout { | |
AlignStart = 'align-start', | |
Default = 'default', | |
} | |
export interface PurpleContent { | |
text?: string; | |
type: StickyType; | |
marks?: FluffyMark[]; | |
attrs?: FluffyAttrs; | |
content?: FluffyContent[]; | |
} | |
export interface FluffyAttrs { | |
collection?: string; | |
height?: number; | |
id?: string; | |
type?: AttrsType; | |
width?: number; | |
url?: string; | |
shortName?: string; | |
text?: string; | |
timestamp?: string; | |
accessLevel?: string; | |
} | |
export enum AttrsType { | |
File = 'file', | |
} | |
export interface FluffyContent { | |
content: TentacledContent[]; | |
type: TentacledType; | |
attrs?: TentacledAttrs; | |
} | |
export interface TentacledAttrs { | |
colwidth?: number[]; | |
layout?: Layout; | |
width?: number; | |
} | |
export interface TentacledContent { | |
text?: string; | |
type: PurpleType; | |
marks?: FluffyMark[]; | |
content?: StickyContent[]; | |
attrs?: StickyAttrs; | |
} | |
export interface StickyAttrs { | |
collection: string; | |
height: number; | |
id: string; | |
type: AttrsType; | |
width: number; | |
} | |
export interface StickyContent { | |
marks?: FluffyMark[]; | |
text?: string; | |
type: FluffyType; | |
attrs?: IndigoAttrs; | |
content?: IndigoContent[]; | |
} | |
export interface IndigoAttrs { | |
color: Color; | |
localId: string; | |
style: string; | |
text: Text; | |
} | |
export enum Color { | |
Green = 'green', | |
Red = 'red', | |
Yellow = 'yellow', | |
} | |
export enum Text { | |
InUse = 'in use', | |
Refactor = 'Refactor', | |
Unused = 'unused', | |
} | |
export interface IndigoContent { | |
content: IndecentContent[]; | |
type: PurpleType; | |
} | |
export interface IndecentContent { | |
text?: string; | |
type: PurpleType; | |
marks?: PurpleMark[]; | |
} | |
export interface PurpleMark { | |
type: MarkType; | |
} | |
export enum MarkType { | |
Code = 'code', | |
Em = 'em', | |
Link = 'link', | |
Strong = 'strong', | |
} | |
export enum PurpleType { | |
BulletList = 'bulletList', | |
HardBreak = 'hardBreak', | |
Media = 'media', | |
Paragraph = 'paragraph', | |
Text = 'text', | |
} | |
export interface FluffyMark { | |
type: MarkType; | |
attrs?: MarkAttrs; | |
} | |
export interface MarkAttrs { | |
href: string; | |
} | |
export enum FluffyType { | |
ListItem = 'listItem', | |
Status = 'status', | |
Text = 'text', | |
} | |
export enum TentacledType { | |
MediaSingle = 'mediaSingle', | |
Paragraph = 'paragraph', | |
TableCell = 'tableCell', | |
TableHeader = 'tableHeader', | |
} | |
export enum StickyType { | |
Date = 'date', | |
Emoji = 'emoji', | |
HardBreak = 'hardBreak', | |
InlineCard = 'inlineCard', | |
ListItem = 'listItem', | |
Media = 'media', | |
Mention = 'mention', | |
TableRow = 'tableRow', | |
Text = 'text', | |
} | |
export enum IndigoType { | |
BulletList = 'bulletList', | |
CodeBlock = 'codeBlock', | |
Heading = 'heading', | |
MediaSingle = 'mediaSingle', | |
OrderedList = 'orderedList', | |
Paragraph = 'paragraph', | |
Rule = 'rule', | |
Table = 'table', | |
} | |
export enum DescriptionType { | |
Doc = 'doc', | |
} | |
export interface Environment { | |
content: EnvironmentContent[]; | |
type: DescriptionType; | |
version: number; | |
} | |
export interface EnvironmentContent { | |
content: HilariousContent[]; | |
type: PurpleType; | |
} | |
export interface HilariousContent { | |
text: string; | |
type: PurpleType; | |
} | |
export interface Issuelink { | |
id: string; | |
inwardIssue?: Parent; | |
self: string; | |
type: TypeClass; | |
outwardIssue?: Parent; | |
} | |
export interface Parent { | |
fields: ParentFields; | |
id: string; | |
key: string; | |
self: string; | |
} | |
export interface ParentFields { | |
issuetype: Issuetype; | |
priority: Priority; | |
status: Status; | |
summary: string; | |
} | |
export interface Issuetype { | |
description: string; | |
hierarchyLevel: number; | |
iconUrl: string; | |
id: string; | |
name: IssuetypeName; | |
self: string; | |
subtask: boolean; | |
avatarId?: number; | |
} | |
export enum IssuetypeName { | |
Bug = 'Bug', | |
Epic = 'Epic', | |
Story = 'Story', | |
SubTask = 'Sub-task', | |
Task = 'Task', | |
} | |
export interface Status { | |
description: string; | |
iconUrl: string; | |
id: string; | |
name: StatusName; | |
self: string; | |
statusCategory: StatusCategory; | |
} | |
export enum StatusName { | |
Closed = 'Closed', | |
InProgress = 'In Progress', | |
InReview = 'In Review', | |
InTest = 'In Test', | |
Ready = 'Ready', | |
ToDo = 'To Do', | |
} | |
export interface StatusCategory { | |
colorName: ColorName; | |
id: number; | |
key: StatusCategoryKey; | |
name: StatusCategoryName; | |
self: string; | |
} | |
export enum ColorName { | |
BlueGray = 'blue-gray', | |
Green = 'green', | |
Yellow = 'yellow', | |
} | |
export enum StatusCategoryKey { | |
Done = 'done', | |
Indeterminate = 'indeterminate', | |
New = 'new', | |
} | |
export enum StatusCategoryName { | |
Done = 'Done', | |
InProgress = 'In Progress', | |
ToDo = 'To Do', | |
} | |
export interface TypeClass { | |
id: string; | |
inward: string; | |
name: string; | |
outward: string; | |
self: string; | |
} | |
export interface Project { | |
avatarUrls: AvatarUrls; | |
id: string; | |
key: ProjectKey; | |
name: ProjectName; | |
projectTypeKey: ProjectTypeKey; | |
self: string; | |
simplified: boolean; | |
} | |
export enum ProjectKey { | |
Volt = 'VOLT', | |
} | |
export enum ProjectName { | |
Voltron = 'Voltron', | |
} | |
export enum ProjectTypeKey { | |
Software = 'software', | |
} | |
export interface Votes { | |
hasVoted: boolean; | |
self: string; | |
votes: number; | |
} | |
export interface Watches { | |
isWatching: boolean; | |
self: string; | |
watchCount: number; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment