UNPKG

pivotal-flow

Version:

🔀 A command-line tool that helps you manage & automate your workflow to use with PivotalTracker.

168 lines (167 loc) • 7.25 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const cli_table_1 = __importDefault(require("cli-table")); const fuzzy_1 = require("fuzzy"); const git_1 = require("../../utils/git"); const common_1 = require("../../utils/pivotal/common"); const questions_1 = require("./questions"); const inquirer_1 = __importDefault(require("../../utils/inquirer")); const string_1 = require("../../utils/string"); const utils_1 = require("../init/utils"); /** * Parse the labels string into a list of labels * @param {string} labels * @example formatLabels('dx, 2.0, test, ') => ['dx', '2.0, 'test'] * @example formatLabels('') => [] */ const parseLabels = (labelNames) => { if (!labelNames) return []; return labelNames .split(',') .map(val => val.trim()) .filter(Boolean); }; /** * Get display-able labels as a string. * @example formatLabels([{ name: 'one', ...}, { name: 'two', ... }]) => 'one, two' */ exports.formatLabels = (labels) => labels .map(label => label.name) .join(', ') .trim(); exports.getNewStoryPayload = ({ ownerId, answers, }) => { const { story_type, name, estimate, labelNames, description = '' } = answers; return { story_type, name, estimate, labels: parseLabels(labelNames || ''), description, owner_ids: [ownerId], // assuming the story is in the current iteration if you are working on it current_state: "planned" /* Planned */, }; }; exports.getStoryDetailsAsTable = (story) => { const table = new cli_table_1.default({ colWidths: [15, 55], head: ['', 'Story Details'], colors: true, style: { head: ['green', 'bold'], }, }); const { name, story_type, labels, estimate, url, description = null } = story; const rows = [ { Title: name }, { Type: common_1.getStoryTypeLabel(story_type) }, { Labels: exports.formatLabels(labels) }, typeof estimate === 'number' && { Estimate: estimate }, { URL: url }, description && description.length && { Description: description }, ].filter(Boolean); table.push(...rows); return table.toString(); }; exports.createNewStory = (client, ownerId) => __awaiter(void 0, void 0, void 0, function* () { const answers = yield inquirer_1.default.prompt(questions_1.WorkOnNewStoryQuestions); const storyPayload = exports.getNewStoryPayload({ ownerId, answers }); return client.createStory(storyPayload); }); /** * Gets the search query for Pivotal Advanced Search to fetch stories based on the parameters. * @param assignedSelf Whether to search for stories assigned to the current user * @param ownerId Current users id */ exports.getSearchStoryQuery = (assignedSelf, ownerId) => { if (assignedSelf) { return `mywork:"${ownerId}" AND state:unstarted,planned`; } // mywork query is much faster than owner:"" or no:owner and other options // refer https://www.pivotaltracker.com/help/articles/advanced_search/ return `mywork:"" AND -owner:"${ownerId}" AND state:unstarted,planned`; }; exports.getExistingStories = (client, owned, ownerId) => __awaiter(void 0, void 0, void 0, function* () { const query = exports.getSearchStoryQuery(owned, ownerId); const { stories: { stories }, } = yield client.getStories(query); if (!(stories && stories.length)) { throw new Error('No stories found. Please ensure that you have stories in your current iteration/backlog and try again.'); } return stories; }); exports.getWorkflow = ({ newStory }) => __awaiter(void 0, void 0, void 0, function* () { if (newStory === true) { return 0 /* New */; } const { selection } = yield inquirer_1.default.prompt(questions_1.PickStoryWorkflowQuestions); return selection; }); exports.selectProjectToCreateStory = () => __awaiter(void 0, void 0, void 0, function* () { const config = yield utils_1.getPivotalFlowConfig(); if (config && config.projects && config.projects.length) { const { projects, pivotalApiToken } = config; // if there's a single project, let's just select it if (projects.length === 1) { const [project] = projects; return { projectId: String(project.id), apiToken: pivotalApiToken }; } const { selectedProject } = yield inquirer_1.default.prompt(questions_1.getPickProjectQuestions(projects)); return { projectId: String(selectedProject), apiToken: pivotalApiToken }; } return { projectId: '', apiToken: '' }; }); exports.getStoryToWorkOn = (client, owner, workflow) => __awaiter(void 0, void 0, void 0, function* () { const { id: ownerId } = owner; if (workflow === 0 /* New */) { return yield exports.createNewStory(client, ownerId); } const owned = workflow === 1 /* Owned */; const stories = yield exports.getExistingStories(client, owned, ownerId); const { story } = yield inquirer_1.default.prompt(questions_1.getSelectStoryFromListQuestions(stories)); return story; }); /** * Returns an autocomplete-source for searching through stories. * @see https://github.com/mokkabonna/inquirer-autocomplete-prompt#example */ exports.getSearchableStoryListSource = ( /** Current story list to search from */ stories) => { const choices = stories.map(story => { const { story_type, name, id } = story; return { name: `[${id}] ${common_1.getStoryTypeIcon(story_type)}: ${string_1.truncate(name)}`, value: story, }; }); const source = (_, input = '') => __awaiter(void 0, void 0, void 0, function* () { const results = fuzzy_1.filter(input, choices, { extract: choice => choice.name, }); return results.map(result => result.original); }); return source; }; exports.startWorkingOnStory = (story) => __awaiter(void 0, void 0, void 0, function* () { const { checkoutBranch, branchName: branchNameInput } = yield inquirer_1.default.prompt(questions_1.getStartStoryQuestions(story)); const { story_type, id } = story; if (checkoutBranch) { const slugifiedBranchName = string_1.slugifyName(branchNameInput); const branchName = common_1.getStoryBranchName(slugifiedBranchName, story_type, id); git_1.checkoutNewBranch(branchName); } return; });