Issue-based recipes

The system provides a full context (issue details, environment) as the input and expects a specific object on the output. Example of a recipe for JIRA that populates specific fields:

!Create method

function create(context) {
    const issue = context.issue;
    const device = context.device;
    const platform = context.platform;
    let summary;

    // Change summary string based on the type of the issue
    if (issue.type == 'crash') {
        summary = `CRASH: ${issue.key}: ${issue.summary || 'No summary'}`
    } else {
        summary = `USER BUG: ${issue.key}: ${issue.summary || 'No summary'}`
    }

    let description = issue.description || '';
    if (description) {
        // Add two newlines to separate other data from the description
        description += '\n\n';
    }

    if (issue.reporter) {
        description += `Reported by ${issue.reporter}\n`;
    }

    description += `Device: ${device.manufacturer} ${device.model}\n`;
    description += `OS: ${platform.version} (${platform.build})\n`;


    description += `View full Bugsee session at: ${issue.url}`;

    return {
        summary: summary,
        description: description,
        custom: {
            components: [{id: 1234}], // Automatically set JIRA component
        }
    };
}

!Update method

function update(context, changes) {
    const issue = context.issue;
    const platform = context.platform;
    const device = context.device;
    const app = context.app;
    const result = {};

    if (changes.description) {
        let description = changes.description.to || '';

        if (description) {
            // Add two newlines to separate other data from the description
            description += '\n\n';
        }

        if (issue.reporter) {
            description += `Reported by ${issue.reporter}\n`;
        }

        description += `View full Bugsee session at: ${issue.url}`;

        result.description = description;
    }

    if (changes.summary) {
        // Change summary string based on the type of the issue
        if (issue.type == 'crash') {
            result.summary = `CRASH: ${issue.key}: ${changes.summary.to || 'No summary'}`
        } else {
            result.summary = `USER BUG: ${issue.key}: ${changes.summary.to || 'No summary'}`
        }
    }

    if (changes.state) {
        // Override state with a specific value, otherwise it will be mapped
        // automatically from Bugsee issue state ('open', 'closed')
        // result.state = 'completed';
    }

    return {
        issue: {
            custom: {
                // Optional
            }
        },
        changes: result
    };
}

The basic structure of a recipe is:

function create(context) {
    // ....

    return {
        // return object with fields that needs to be overridden
        // allowed properties: 'summary', 'description', 'reporter', 'priority', 'labels'
        custom: {
            // custom, service specific fields, refer to a documentation of a particular service for custom fields
            // for a specific service.
        }
    };
}

function update(context, changes) {
    const result = {};
    // ...


    if (changes.description) {
        // 'changes' object contains fields that were changed with old and new values ({ from: <old_value>, to: <new_value> }).
        // Simply ensure any of them is within 'changes' and react accordingly
    }

    return {
        issue: {
            custom: {
                // custom, service specific fields, refer to a documentation of a particular service for custom fields
                // for a specific service.
            }
        },
        changes: result
    };
}

Context

The input to the create() and update() methods is a full context of the issue about to be created:

{
    integration: {
        provider: 'github'   // provider name that current recipe is executed for
    },
    issue: {
        key: 'IOS-123', // Unique key of the issue,
        summary: 'Something does not work', // Short summary of the issue
        description: '', // Description of the issue,
        created_on: '2017-01-19 23:29:32.021Z', // Date of creation
        type: 'bug', // Type of the issue, 'bug', 'crash' or 'error'
        state: 'open', // Issue state. Either 'open' or 'closed'
        labels: [], // String labels assigned to the issue
        url: 'https://app.bugsee.com/IOS/IOS-123', // URL of the issue
        priority: 5, // Automatically guessed priority for the current remote service provider (JIRA, GitHub, etc.)
        severity: 5, // Original Bugsee severity (1 through 5)
        reporter: 'John Smith (john.smith@example.com)', // User who reported an issue
        attributes: {
            // Session/user attributes
        }
    },
    app: {
        name: 'My Awesome App', // Application name
        version: '1.0.2',
        build: '12345'
    },
    device: {
        name: 'My precious',
        manufacturer: 'Samsung',
        model: 'GT-1656',
        model_name: 'Samsung Galaxy S6',
        screen: {
            width: 1440,
            height: 2560,
            scale: 1,
            dpiX: 577,
            dpiY: 577
        }
    },
    platform: {
        type: 'android', // Type: 'ios', 'android'
        version: '7.2.3', // OS version
        build: 'BG1234', // OS build
        release: 'Nougat' // Release name
    },
    // exists for manually pushed issues
    submitter: {
        name: 'John Doe',
        email: 'name@example.com'
    },
    // for issues reported by chrome extension
    browser: {
        type: 'Chrome',
        version: '66.0.3359.139'
    }
}

Changes

Method update(context, changes) along with context receives changes object. It has the following structure:

{
    "<field>": {
        "from": "<old_value>",
        "to": "<new_value>"
    }
}

changes object contains the fields (and their values) that were changed. Be aware, that all the changes are related to issue object in context. And in turn, context itself contains all the new values that are present in changes.

So, when implementing update flow, you should check whether some field was actually changed:

function update(context, changes) {
    const result = {};
    // ...

    if (changes.summary) {
        // summary was changed, update it here according to your needs
        result.summary = `UPD[summary]: ${changes.summary.from} -> ${changes.summary.to}`;
    }

    // ...
    return {
        issue: {
            custom: {}
        },
        changes: result
    };
}