├── .editorconfig ├── .eslintrc.json ├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CONTRIBUTE.md ├── Dockerfile ├── Makefile ├── README.md ├── check ├── ci ├── Makefile ├── Vagrantfile ├── pipelines │ ├── example.yml │ └── pipeline.yml ├── resources │ └── config.yml.example └── scripts │ ├── run-tests │ └── sync-version ├── circle.yml ├── in ├── out ├── package-lock.json ├── package.json ├── prettierrc.json ├── spec ├── addComments.js ├── addWatchers.js ├── createOrUpdateIssue.js ├── out.js ├── processTransitions.js ├── resources │ ├── concourseInput.js │ ├── concourseInputSubTask.js │ ├── jira-resource.postman_collection.json │ ├── jiraDetails.js │ ├── sample.out │ └── sample.version └── searchBySummary.js ├── src ├── addComments.js ├── addWatchers.js ├── createIssue.js ├── out.js ├── processTransitions.js ├── searchIssues.js ├── updateIssue.js └── utils │ ├── customFieldFactory.js │ ├── debugResponse.js │ ├── freeTextCustomField.js │ ├── replaceTextFileString.js │ ├── requestIssue.js │ └── selectListCustomField.js └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | indent_style = space 12 | indent_size = 4 13 | 14 | # Matches the exact files either package.json or .travis.yml 15 | [{package.json,package-lock.json.travis.yml}] 16 | indent_size = 2 17 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 6, 4 | "sourceType": "module", 5 | "ecmaFeatures": { 6 | "modules": true, 7 | "arrowFunctions": true, 8 | "blockBindings": true 9 | } 10 | }, 11 | "env": { 12 | "es6": true, 13 | "mocha": true, 14 | "node": true 15 | }, 16 | "rules": { 17 | "arrow-parens": [ 18 | 1, 19 | "always" 20 | ], 21 | "arrow-spacing": [ 22 | 1, 23 | { 24 | "before": true, 25 | "after": true 26 | } 27 | ], 28 | "comma-dangle": [ 29 | 0, 30 | "always-multiline" 31 | ], 32 | "linebreak-style": [ 33 | 2, 34 | "unix" 35 | ], 36 | "lines-around-comment": [ 37 | 2, 38 | { 39 | "beforeBlockComment": true, 40 | "beforeLineComment": true 41 | } 42 | ], 43 | "no-alert": 2, 44 | "no-console": 1, 45 | "no-else-return": 2, 46 | "no-extra-semi": 2, 47 | "no-multi-spaces": 2, 48 | "no-undef": 0, 49 | "no-undefined": 2, 50 | "no-unused-vars": 2, 51 | "strict": [ 52 | 2, 53 | "global" 54 | ], 55 | "max-depth": [ 56 | 2, 57 | 4 58 | ], 59 | "max-len": [ 60 | 2, 61 | 120, 62 | 120, 63 | { 64 | "ignoreComments": true, 65 | "ignoreUrls": true 66 | } 67 | ], 68 | "quotes": [ 69 | 2, 70 | "single" 71 | ], 72 | "valid-jsdoc": 1, 73 | "valid-typeof": 1 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /yarn-error.log 3 | /npm-debug.log 4 | /coverage 5 | /reports 6 | 7 | .idea 8 | .vagrant 9 | 10 | *.swp 11 | config.yml 12 | fly 13 | real.json 14 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | build: 2 | nodes: 3 | analysis: 4 | tests: 5 | override: 6 | - js-scrutinizer-run 7 | tests: true 8 | tests: 9 | override: 10 | - 11 | command: 'node ./node_modules/.bin/istanbul cover --report clover --dir reports/coverage src/*.js' 12 | coverage: 13 | file: 'reports/coverage/clover.xml' 14 | format: 'clover' 15 | checks: 16 | javascript: true 17 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | - "lts/*" 5 | cache: 6 | directories: 7 | - "node_modules" 8 | branches: 9 | except: 10 | - version 11 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # Contribute 2 | 3 | ## Environment 4 | 5 | ### Concourse ci 6 | 7 | 1. Change to the `ci` directory `cd cd` 8 | 2. Start a concourse ci test instance with vagrant `vagrant up` 9 | 3. Install concourse ci cli tool with `make fly-osx` or `make fly-linux` 10 | 4. Login to your concourse ci test instance `make login` (password: _changeme_) 11 | 5. Create you own config file `cp resources/config.yml.example resources/config.yml` and configure your credentials 12 | 6. Setup a test pipeline `make pipeline` 13 | 7. Setup another test pipeline `make example` 14 | 8. Unpause your pipelines `make unpause` 15 | 16 | ### Concourse resource 17 | 18 | 1. Install all dependencies `npm install` and `sudo gem install terminal-notifier` (for OSX notifications) 19 | 2. Run the tests `npm test` 20 | 3. Run the tests with a change watcher `npm run test-watch` 21 | 4. Run the tests with a change watcher and debugger `npm run test-watch-debug` 22 | 23 | ## Contribute 24 | 25 | 1. Make a fork 26 | 2. Run all tests 27 | 3. Create a pull request 28 | 29 | ## Custom build 30 | 31 | 1. docker build --no-cache -t danrspencer/jira-resource . 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.7 2 | 3 | LABEL maintainer="Dan Spencer" \ 4 | authors="Dan Spencer, André Lademann" \ 5 | version="0.0.4" \ 6 | description="Jira ticket resource for concourse ci." 7 | 8 | RUN apk add --no-cache \ 9 | bash \ 10 | nodejs 11 | 12 | COPY check /opt/resource/check 13 | COPY in /opt/resource/in 14 | COPY out /opt/resource/out 15 | COPY src/ /opt/resource/src/ 16 | COPY package.json /opt/resource/package.json 17 | 18 | RUN chmod +x /opt/resource/out /opt/resource/in /opt/resource/check 19 | 20 | WORKDIR /opt/resource/ 21 | 22 | RUN npm install 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all build push 2 | 3 | all: build 4 | make push 5 | @echo "=== DONE ===" 6 | 7 | build: 8 | docker build --no-cache -t vergissberlin/jira-resource . 9 | 10 | push: 11 | docker push vergissberlin/jira-resource 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### I'm no longer actively maintaining this repository and I won't be performing any more development myself for the foreseeable future. 2 | 3 | ### I am happy to look over and merge any pull requests raised. 4 | --- 5 | 6 | # Jira Ticket Resource 7 | 8 | [![Build Status](https://travis-ci.org/vergissberlin/jira-resource.svg?branch=master)](https://travis-ci.org/vergissberlin/jira-resource) 9 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/vergissberlin/jira-resource/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/vergissberlin/jira-resource/?branch=master) 10 | [![Code Intelligence Status](https://scrutinizer-ci.com/g/vergissberlin/jira-resource/badges/code-intelligence.svg?b=master)](https://scrutinizer-ci.com/code-intelligence) 11 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f2ff0bbe8ec746b7af07d3bb088e0787)](https://www.codacy.com/app/andre_1725/jira-resource?utm_source=github.com&utm_medium=referral&utm_content=vergissberlin/jira-resource&utm_campaign=Badge_Grade) 12 | 13 | Create and update Jira tickets via Concourse 14 | 15 | ## Quick Example 16 | 17 | ```yaml 18 | - name: update-jira 19 | plan: 20 | - put: jira 21 | params: 22 | issue_type: Change 23 | issue_key: BUILD-11 24 | summary: Build v1.0.1 25 | fields: 26 | description: With some text 27 | ``` 28 | 29 | ## Source Configuration 30 | 31 | Newer versions of Jira no longer support username and password credentials for its basic auth method and have switched to email and API token instead. You will need to generate an API token from an account with sufficient permissions to perform the actions this plugin supports. See [the Jira documentation](https://developer.atlassian.com/cloud/jira/platform/jira-rest-api-basic-authentication/) for more information. 32 | 33 | ```yaml 34 | resources: 35 | - name: jira 36 | type: jira-resource 37 | source: 38 | url: https://xxxxx.atlassian.net 39 | email: xxxxx 40 | apitoken: xxxxx 41 | project: APROJECT 42 | ``` 43 | 44 | If you are using an older version of Jira Server that still supports the deprecated password-based basic auth, you can keep using the previous set of source settings as below 45 | 46 | ```yaml 47 | resources: 48 | - name: jira 49 | type: jira-resource 50 | source: 51 | url: https://xxxxx.atlassian.net 52 | username: xxxxx 53 | password: xxxxx 54 | project: APROJECT 55 | ``` 56 | 57 | ## Resource Type Configuration 58 | 59 | ```yaml 60 | resource_types: 61 | - name: jira-resource 62 | type: docker-image 63 | source: 64 | repository: danrspencer/jira-resource 65 | tag: latest 66 | ``` 67 | 68 | ## Behavior 69 | 70 | ### `out`: Creates, updates and transitions a Jira ticket 71 | 72 | #### $TEXT 73 | 74 | The `summary`, `fields` and `custom_fields` can be either a string value, or an object with `text` / `file` fields. If just the `file` or `text` is specified it is used to populate the field, if both `file` and `text` are specified then the file is substituted in to replace $FILE in the text. 75 | 76 | e.g. 77 | 78 | ```yaml 79 | description: An awesome ticket 80 | ------- 81 | description: 82 | file: messages/this-will-be-the-description 83 | ------- 84 | description: 85 | text: | 86 | Substitute text into this messaage 87 | 88 | $FILE 89 | file: messages/jira-message 90 | ------- 91 | description: 92 | text: | 93 | Substitute text into this messaage 94 | ``` 95 | 96 | #### $NOW 97 | 98 | If a date field needs to be populated with a date relative to current time `$NOW` can be used. The datetime can be modified relative to the current date. 99 | 100 | e.g. 101 | 102 | ``` 103 | $NOW 104 | # One hour ahead 105 | $NOW+1h 106 | # Yesterday 107 | $NOW-1d 108 | ``` 109 | 110 | _KEY_ 111 | 112 | - `years y` 113 | - `weeks w` 114 | - `days d` 115 | - `hours h` 116 | - `minutes m` 117 | - `seconds s` 118 | 119 | #### Parameters 120 | 121 | - `issue_key`: Just using for searching / updating existing issue. For searching / updating issue, priority is higher than searching by `summary`. 122 | 123 | ```yaml 124 | issue_key: BUILD-11 125 | ------- 126 | issue_key: BUILD-11,BUILD-12,BUILD-13 127 | ------- 128 | issue_key: 129 | file: issue/key 130 | ------- 131 | issue_key: 132 | text: BUILD-$FILE 133 | file: issue/key 134 | ------- 135 | issue_key: 136 | text: BUILD-11 137 | ``` 138 | 139 | - `summary`: _required for create issue_ The summary of the Jira Ticket. This is used as a unique identifier for the ticket for the purpose of updating / modifying. As such it's recommended you include something unique in the summary, such as the build version. For searching/updating issue, priority is lower than searching by `summary`. 140 | 141 | 142 | ```yaml 143 | summary: Ticket Summary 144 | ------- 145 | summary: 146 | text: Build v$FILE 147 | file: version/version 148 | ``` 149 | 150 | - `issue_type`: The issue type for the ticket 151 | 152 | ```yaml 153 | issue_type: Bug 154 | ``` 155 | 156 | - `fields`: A list of fields to be set with specified values 157 | 158 | ```yaml 159 | fields: 160 | description: 161 | text: | 162 | Routine Release 163 | 164 | $FILE 165 | file: messages/jira-release-notes 166 | environment: Prod 167 | duedate: $NOW+1h 168 | ``` 169 | 170 | - `custom_fields`: A list of custom fields to update. The id and value are used to update the custom field. You can also specify the type of custom field. Currently supported types are: 171 | - Default (no type specified): set a `FreeTextField` custom field. 172 | - `selectlist`: set a `SelectList` custom field. Can set its value by ID with the `value_id` key or by value with the `value` key. 173 | 174 | Have a look at the [JIRA API documentation](https://developer.atlassian.com/server/jira/platform/jira-rest-api-examples/#creating-an-issue-examples) for more information about the different field types and their content format. 175 | 176 | The property title doesn't matter, it's purpose is to make the pipeline yml more understandable. 177 | 178 | ```yaml 179 | custom_fields: 180 | sample_freetext_field: 181 | id: 10201 182 | value: Something 183 | sample_selectlist_field: 184 | id: 10202 185 | type: selectlist 186 | value: Some selectlist value 187 | sample_selectlist_id_field: 188 | id: 10203 189 | type: selectlist 190 | value_id: "123" 191 | ``` 192 | 193 | - `watchers`: An array of usernames to be added as watchers to the ticket 194 | 195 | ```yaml 196 | watchers: 197 | - dave 198 | - amier 199 | - lauren 200 | ``` 201 | 202 | - `transitions`: An array of string values for the ticket to be moved through. The transitions are conducted in the order specified. 203 | 204 | ```yaml 205 | transitions: 206 | - Submit 207 | - In Dev 208 | ``` 209 | 210 | - `comments`: Add comments. 211 | 212 | ```yaml 213 | comments: 214 | - content: 215 | text: | 216 | New comment 217 | $FILE 218 | file: messages/jira-release-notes 219 | - content: 220 | file: messages/jira-release-notes 221 | - content: 222 | text: | 223 | New comment 224 | - content: New comment 225 | ``` 226 | 227 | - `search_filters`: Custom filter when searching issues. 228 | 229 | ```yaml 230 | search_filters: 231 | status: Done 232 | type: Bug 233 | ``` 234 | 235 | #### Order of execution 236 | 237 | When executing the Jira job the ticket is updated in the following order: 238 | 239 | - Search for existing issue matching `summary` given 240 | - Create ticket / update ticket 241 | - Add watchers 242 | - Perform transitions 243 | - Add comments 244 | 245 | If you need to perform actions in a different order, for example, transitions before adding watchers then multiple jobs are required. 246 | 247 | e.g. 248 | 249 | ```yaml 250 | # Create ticket and Submit 251 | - put: jira 252 | params: 253 | issue_type: Change 254 | summary: 255 | text: Site v$FILE 256 | file: version/version 257 | transitions: 258 | - Submit 259 | 260 | # Create sub task 261 | - put: jira 262 | params: 263 | parent: ABC 264 | issue_type: Sub: Task 265 | summary: 266 | text: Site v$FILE 267 | file: version/version 268 | 269 | # Add a watcher then move to in dev 270 | - put: jira 271 | params: 272 | summary: 273 | text: Site v$FILE 274 | file: version/version 275 | watchers: 276 | - dan 277 | transitions: 278 | - In Dev 279 | ``` 280 | 281 | #### Real world example 282 | 283 | ```yaml 284 | jobs: 285 | - name: start-release 286 | plan: 287 | - get: code-base 288 | trigger: true 289 | - get: version 290 | - task: write-release-notes 291 | config: 292 | platform: linux 293 | image_resource: 294 | source: 295 | repository: travix/base-alpine-git 296 | type: docker-image 297 | inputs: 298 | - name: version 299 | - name: code-base 300 | run: 301 | path: code-base/ci/scripts/write-release-notes 302 | outputs: 303 | - name: messages 304 | - put: jira 305 | params: 306 | issue_type: Change 307 | summary: 308 | text: Build v$FILE 309 | file: version/version 310 | fields: 311 | description: 312 | text: | 313 | Routine Release 314 | 315 | $FILE 316 | 317 | _Generated by Concourse_ 318 | file: messages/jira-release-notes 319 | environment: Prod 320 | duedate: $NOW+1h 321 | custom_fields: 322 | how_awesome: 323 | id: 10201 324 | value: Very! 325 | transitions: 326 | - Submit 327 | - put: jira 328 | params: 329 | summary: 330 | text: Build v$FILE 331 | file: version/version 332 | watchers: 333 | - dave 334 | - amier 335 | - lauren 336 | transitions: 337 | - Approve 338 | comments: 339 | - content: New comment by consourse 340 | 341 | resources: 342 | - name: code-base 343 | type: git 344 | source: 345 | uri: git@github.com:danrspencer/jira-resource.git 346 | branch: master 347 | private_key: { { private-repo-key } } 348 | 349 | - name: version 350 | type: semver 351 | source: 352 | driver: git 353 | uri: git@github.com:danrspencer/jira-resource.git 354 | branch: version 355 | private_key: { { private-repo-key } } 356 | file: version 357 | 358 | - name: jira 359 | type: jira-resource 360 | source: 361 | url: https://jira.atlassian.net 362 | email: { { jira-email } } 363 | apitoken: { { jira-api-token } } 364 | project: JR 365 | 366 | resource_types: 367 | - name: jira-resource 368 | type: docker-image 369 | source: 370 | repository: danrspencer/jira-resource 371 | tag: latest 372 | ``` 373 | -------------------------------------------------------------------------------- /check: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "[]" 4 | -------------------------------------------------------------------------------- /ci/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: fly-osx fly-linux login pipeline example unpause destroy 2 | 3 | fly-osx: 4 | curl --user concourse -o fly 'http://192.168.100.4:8080/api/v1/cli?arch=amd64&platform=darwin' 5 | chmod +x fly 6 | 7 | fly-linux: 8 | curl --user concourse -o fly 'http://192.168.100.4:8080/api/v1/cli?arch=amd64&platform=linux' 9 | chmod +x fly 10 | 11 | login: 12 | ./fly --target main-jira login --concourse-url http://192.168.100.4:8080 13 | 14 | pipeline: 15 | ./fly --target main-jira set-pipeline --pipeline jira-resource \ 16 | --config pipelines/pipeline.yml \ 17 | --load-vars-from resources/config.yml \ 18 | --var "private-repo-key=`cat ~/.ssh/concourse`" \ 19 | -n 20 | 21 | example: 22 | ./fly --target main-jira set-pipeline --pipeline jira-resource-example \ 23 | --config pipelines/example.yml \ 24 | --load-vars-from resources/config.yml \ 25 | --var "private-repo-key=`cat ~/.ssh/concourse`" \ 26 | -n 27 | unpause: 28 | ./fly -t main-jira unpause-pipeline -p jira-resource 29 | ./fly -t main-jira unpause-pipeline -p jira-resource-example 30 | 31 | destroy: 32 | ./fly -t main-jira destroy-pipeline -p jira-resource -n 33 | ./fly -t main-jira destroy-pipeline -p jira-resource-example -n 34 | -------------------------------------------------------------------------------- /ci/Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Plugins 5 | unless Vagrant.has_plugin?("vagrant-vbguest") 6 | system("vagrant plugin install vagrant-vbguest") 7 | puts "Dependencies installed, please try the command again." 8 | exit 9 | end 10 | 11 | # All Vagrant configuration is done below. The "2" in Vagrant.configure 12 | # configures the configuration version (we support older styles for 13 | # backwards compatibility). Please don't change it unless you know what 14 | # you're doing. 15 | Vagrant.configure(2) do |config| 16 | # The most common configuration options are documented and commented below. 17 | # For a complete reference, please see the online documentation at 18 | # https://docs.vagrantup.com. 19 | 20 | # https://app.vagrantup.com/concourse/boxes/lite 21 | config.vm.box = "concourse/lite" 22 | 23 | # Disable automatic box update checking. If you disable this, then 24 | # boxes will only be checked for updates when the user runs 25 | # `vagrant box outdated`. This is not recommended. 26 | # config.vm.box_check_update = false 27 | 28 | # Create a forwarded port mapping which allows access to a specific port 29 | # within the machine from a port on the host machine. In the example below, 30 | # accessing "localhost:8080" will access port 80 on the guest machine. 31 | # config.vm.network "forwarded_port", guest: 80, host: 8080 32 | 33 | # Create a private network, which allows host-only access to the machine 34 | # using a specific IP. 35 | # config.vm.network "private_network", ip: "192.168.33.10" 36 | 37 | # Create a public network, which generally matched to bridged network. 38 | # Bridged networks make the machine appear as another physical device on 39 | # your network. 40 | # config.vm.network "public_network" 41 | 42 | # Share an additional folder to the guest VM. The first argument is 43 | # the path on the host to the actual folder. The second argument is 44 | # the path on the guest to mount the folder. And the optional third 45 | # argument is a set of non-required options. 46 | # config.vm.synced_folder "../data", "/vagrant_data" 47 | 48 | # Provider-specific configuration so you can fine-tune various 49 | # backing providers for Vagrant. These expose provider-specific options. 50 | # Example for VirtualBox: 51 | # 52 | # config.vm.provider "virtualbox" do |vb| 53 | # # Display the VirtualBox GUI when booting the machine 54 | # vb.gui = true 55 | # 56 | # # Customize the amount of memory on the VM: 57 | # vb.memory = "1024" 58 | # end 59 | # 60 | # View the documentation for the provider you are using for more 61 | # information on available options. 62 | 63 | # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies 64 | # such as FTP and Heroku are also available. See the documentation at 65 | # https://docs.vagrantup.com/v2/push/atlas.html for more information. 66 | # config.push.define "atlas" do |push| 67 | # push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME" 68 | # end 69 | 70 | # Enable provisioning with a shell script. Additional provisioners such as 71 | # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the 72 | # documentation for more information about their specific syntax and use. 73 | # config.vm.provision "shell", inline: <<-SHELL 74 | # sudo apt-get update 75 | # sudo apt-get install -y apache2 76 | # SHELL 77 | end 78 | -------------------------------------------------------------------------------- /ci/pipelines/example.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | resources: 4 | - name: git-jira-resource 5 | type: git 6 | source: 7 | uri: git@github.com:((git-username))/jira-resource.git 8 | branch: master 9 | private_key: ((private-repo-key)) 10 | 11 | - name: jira 12 | type: jira 13 | source: 14 | url: ((jira-url)) 15 | username: ((jira-username)) 16 | password: ((jira-password)) 17 | project: ((jira-project)) 18 | 19 | resource_types: 20 | - name: jira 21 | type: docker-image 22 | source: 23 | repository: ((git-username))/jira-resource 24 | tag: latest 25 | 26 | jobs: 27 | - name: update-jira 28 | plan: 29 | - get: git-jira-resource 30 | trigger: true 31 | - put: jira 32 | params: 33 | issue_type: ((jira-issuetype)) 34 | summary: Example jira ticket $NOW 35 | fields: 36 | description: 37 | file: git-jira-resource/spec/resources/sample.out 38 | text: Static text - $FILE 39 | environment: PROD 40 | duedate: $NOW+1H 41 | transitions: 42 | - ((jira-transition)) 43 | - put: jira 44 | params: 45 | parent: ((jira-parent)) 46 | issue_type: ((jira-issuetype)) 47 | summary: Example jira sub ticket from concourse $NOW 48 | fields: 49 | description: 50 | file: git-jira-resource/spec/resources/sample.out 51 | text: Static text - $FILE 52 | environment: PROD 53 | duedate: $NOW+1H 54 | -------------------------------------------------------------------------------- /ci/pipelines/pipeline.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | resources: 4 | - name: git-jira-resource 5 | type: git 6 | source: 7 | uri: git@github.com:((git-username))/jira-resource.git 8 | branch: master 9 | private_key: ((private-repo-key)) 10 | ignore_paths: [ package.json ] 11 | 12 | - name: version 13 | type: semver 14 | source: 15 | driver: git 16 | uri: git@github.com:((git-username))/jira-resource.git 17 | branch: version 18 | private_key: ((private-repo-key)) 19 | file: version 20 | 21 | - name: docker-jira-resource 22 | type: docker-image 23 | source: 24 | repository: ((docker-username))/jira-resource 25 | username: ((docker-username)) 26 | password: ((docker-password)) 27 | email: ((docker-email)) 28 | 29 | jobs: 30 | - name: test 31 | plan: 32 | - get: git-jira-resource 33 | trigger: true 34 | - task: run-tests 35 | config: 36 | platform: linux 37 | image_resource: 38 | source: 39 | repository: ((docker-username))/jira-resource 40 | type: docker-image 41 | inputs: 42 | - name: git-jira-resource 43 | run: 44 | path: git-jira-resource/ci/scripts/run-tests 45 | 46 | - name: build 47 | plan: 48 | - get: git-jira-resource 49 | trigger: true 50 | passed: [ test ] 51 | - put: version 52 | params: 53 | bump: patch 54 | - put: docker-jira-resource 55 | params: 56 | build: git-jira-resource 57 | tag: version/version 58 | tag_as_latest: true 59 | 60 | - name: major 61 | plan: 62 | - put: version 63 | params: 64 | bump: major 65 | 66 | - name: minor 67 | plan: 68 | - put: version 69 | params: 70 | bump: minor 71 | -------------------------------------------------------------------------------- /ci/resources/config.yml.example: -------------------------------------------------------------------------------- 1 | jira-url: www.jira.com 2 | jira-username: username 3 | jira-password: password 4 | jira-project: ABC 5 | jira-parent: ABC-1 6 | jira-issuetype: Task 7 | jira-transition: Estimated 8 | 9 | docker-username: username 10 | docker-password: password 11 | docker-email: awesome@somewere.io 12 | 13 | git-username: awesome 14 | -------------------------------------------------------------------------------- /ci/scripts/run-tests: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd git-jira-resource 4 | 5 | npm install 6 | npm test 7 | -------------------------------------------------------------------------------- /ci/scripts/sync-version: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | VERSION=$(cat version/version) 4 | 5 | sed -i '' "s#\"version\": \"[0-9.]*\"#\"version\": \"$VERSION\"#g" git-jira-resource/package.json 6 | sed -i '' "s#\"version\": \"[0-9.]*\"#\"version\"= \"$VERSION\"#g" git-jira-resource/Dockerfile 7 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: node 4 | 5 | test: 6 | post: 7 | - node node_modules/mocha/bin/mocha ./spec/ --async-only --reporter mocha-junit-reporter: 8 | environment: 9 | MOCHA_FILE: $CIRCLE_TEST_REPORTS/junit/test-results.xml 10 | - node node_modules/mocha/bin/mocha ./spec/ --async-only --reporter mocha-simple-html-reporter --reporter-options output=$CIRCLE_TEST_REPORTS/mocha.html: 11 | - node ./node_modules/.bin/istanbul cover --report clover --dir reports/coverage src/*.js cover --report clover --dir $CIRCLE_TEST_REPORTS/coverage src/*.js 12 | 13 | dependencies: 14 | cache_directories: 15 | - "node_modules" 16 | -------------------------------------------------------------------------------- /in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo '{"version":{"ref":"none"}}' 4 | -------------------------------------------------------------------------------- /out: -------------------------------------------------------------------------------- 1 | #!/usr/bin/node 2 | 3 | // This must be set BEFORE out.js is required for the debug info to show up 4 | process.env.DEBUG = '*' 5 | 6 | const out = require('./src/out.js') 7 | 8 | let putDir = process.argv[2] 9 | 10 | process.stdin.resume() 11 | process.stdin.setEncoding('utf8') 12 | process.stdin.on('data', function (data) { 13 | out(JSON.parse(data), putDir, (error, result) => { 14 | if ( error ) { 15 | process.stderr.write(error.message) 16 | process.exit(1) 17 | } 18 | 19 | process.stdout.write(JSON.stringify(result)) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jira-resource", 3 | "version": "1.0.0", 4 | "description": "Jira ticket resource for concourse ci", 5 | "main": "out.js", 6 | "scripts": { 7 | "precommit": "prettier-eslint src/**/*.js --write && git add .", 8 | "test": "node node_modules/mocha/bin/mocha ./spec/ --async-only", 9 | "test-watch": "node node_modules/mocha/bin/mocha ./spec/ --async-only --watch --growl", 10 | "test-watch-debug": "DEBUG=jira-resource node node_modules/mocha/bin/mocha ./spec/ --async-only --watch --growl", 11 | "test-report": "node node_modules/mocha/bin/mocha ./spec/ --async-only --reporter mocha-junit-reporter --reporter-options mochaFile=reports/junit/mocha.xml", 12 | "test-report-html": "node node_modules/mocha/bin/mocha ./spec/ --async-only --reporter mocha-simple-html-reporter --reporter-options output=reports/mocha.html", 13 | "test-report-coverage": "node ./node_modules/.bin/istanbul cover --report clover --dir reports/coverage src/*.js", 14 | "test-with-coverage": "node ./node_modules/.bin/istanbul cover _mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | codacy-coverage && rm -rf ./coverage" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/danrspencer/jira-resource.git" 19 | }, 20 | "author": "", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/danrspencer/jira-resource/issues" 24 | }, 25 | "homepage": "https://github.com/danrspencer/jira-resource#readme", 26 | "devDependencies": { 27 | "chai": "^3.5.0", 28 | "eslint": "^4.19.1", 29 | "husky": "^0.14.3", 30 | "istanbul": "^0.4.5", 31 | "mocha": "^5.2.0", 32 | "mocha-junit-reporter": "^1.17.0", 33 | "mocha-lcov-reporter": "^1.3.0", 34 | "mocha-simple-html-reporter": "^1.1.0", 35 | "nock": "^9.2.6", 36 | "prettier": "^1.13.4", 37 | "prettier-eslint": "^8.8.2", 38 | "prettier-eslint-cli": "^4.7.1", 39 | "pretty-quick": "^1.6.0", 40 | "request-debug": "^0.2.0" 41 | }, 42 | "dependencies": { 43 | "async": "^2.0.0-rc.6", 44 | "debug": "^2.2.0", 45 | "lodash": "^4.17.16", 46 | "moment": "^2.13.0", 47 | "request": "^2.72.0" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "es5", 3 | "tabWidth": 4, 4 | "semi": true, 5 | "singleQuote": true 6 | } 7 | -------------------------------------------------------------------------------- /spec/addComments.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const expect = chai.expect; 3 | 4 | const nock = require("nock"); 5 | 6 | const addComments = require("../src/addComments.js"); 7 | 8 | const jira = require("./resources/jiraDetails.js"); 9 | const concourseInput = require("./resources/concourseInput.js"); 10 | 11 | nock.disableNetConnect(); 12 | 13 | describe("addComments", () => { 14 | let issue = { 15 | id: "15805", 16 | key: "ATP-1", 17 | self: jira.url + "rest/api/2/issue/15805" 18 | }; 19 | 20 | beforeEach(() => { 21 | nock.cleanAll(); 22 | }); 23 | 24 | it("adds a comment a new issue", done => { 25 | let input = concourseInput(); 26 | input.params.comments = [{content: "new comment"}]; 27 | 28 | let addComment = nock(jira.url) 29 | .post("/rest/api/2/issue/15805/comment/", {body: "new comment"}) 30 | .basicAuth({ 31 | user: jira.user, 32 | pass: jira.token 33 | }) 34 | .reply(201); 35 | 36 | addComments("", issue, input.source, input.params, () => { 37 | expect(addComment.isDone()).to.be.true; 38 | done(); 39 | }); 40 | }); 41 | 42 | it("adds multiple comments", done => { 43 | let input = concourseInput(); 44 | input.params.comments = [ 45 | {content: "new comment 1"}, 46 | {content: "new comment 2"}, 47 | {content: "new comment 3"}, 48 | ]; 49 | 50 | let addComment = nock(jira.url) 51 | .post("/rest/api/2/issue/15805/comment/", {body: "new comment 1"}) 52 | .basicAuth({ 53 | user: jira.user, 54 | pass: jira.token 55 | }) 56 | .reply(201) 57 | .post("/rest/api/2/issue/15805/comment/", {body: "new comment 2"}) 58 | .basicAuth({ 59 | user: jira.user, 60 | pass: jira.token 61 | }) 62 | .reply(201) 63 | .post("/rest/api/2/issue/15805/comment/", {body: "new comment 3"}) 64 | .basicAuth({ 65 | user: jira.user, 66 | pass: jira.token 67 | }) 68 | .reply(201); 69 | 70 | addComments("", issue, input.source, input.params, () => { 71 | expect(addComment.isDone()).to.be.true; 72 | done(); 73 | }); 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /spec/addWatchers.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const expect = chai.expect; 3 | 4 | const nock = require("nock"); 5 | 6 | const addWatchers = require("../src/addWatchers.js"); 7 | 8 | const jira = require("./resources/jiraDetails.js"); 9 | const concourseInput = require("./resources/concourseInput.js"); 10 | 11 | nock.disableNetConnect(); 12 | 13 | describe("addWatchers", () => { 14 | let issue = { 15 | id: "15805", 16 | key: "ATP-1", 17 | self: jira.url + "rest/api/2/issue/15805" 18 | }; 19 | 20 | beforeEach(() => { 21 | nock.cleanAll(); 22 | }); 23 | 24 | it("adds a watcher a new issue", done => { 25 | let input = concourseInput(); 26 | input.params.watchers = ["user1"]; 27 | 28 | let addWatcher = nock(jira.url) 29 | .post("/rest/api/2/issue/15805/watchers/", '"user1"') 30 | .basicAuth({ 31 | user: jira.user, 32 | pass: jira.token 33 | }) 34 | .reply(204); 35 | 36 | addWatchers(issue, input.source, input.params, () => { 37 | expect(addWatcher.isDone()).to.be.true; 38 | done(); 39 | }); 40 | }); 41 | 42 | it("adds multiple watchers", done => { 43 | let input = concourseInput(); 44 | input.params.watchers = ["user1", "user2", "user3"]; 45 | 46 | let addWatcher = nock(jira.url) 47 | .post("/rest/api/2/issue/15805/watchers/", '"user1"') 48 | .basicAuth({ 49 | user: jira.user, 50 | pass: jira.token 51 | }) 52 | .reply(204) 53 | .post("/rest/api/2/issue/15805/watchers/", '"user2"') 54 | .basicAuth({ 55 | user: jira.user, 56 | pass: jira.token 57 | }) 58 | .reply(204) 59 | .post("/rest/api/2/issue/15805/watchers/", '"user3"') 60 | .basicAuth({ 61 | user: jira.user, 62 | pass: jira.token 63 | }) 64 | .reply(204); 65 | 66 | addWatchers(issue, input.source, input.params, () => { 67 | expect(addWatcher.isDone()).to.be.true; 68 | done(); 69 | }); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /spec/createOrUpdateIssue.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const expect = chai.expect; 3 | 4 | const moment = require("moment"); 5 | const nock = require("nock"); 6 | 7 | const createIssue = require("../src/createIssue.js"); 8 | const updateIssue = require("../src/updateIssue.js"); 9 | 10 | const jira = require("./resources/jiraDetails.js"); 11 | const concourseInput = require("./resources/concourseInput.js"); 12 | const concourseInputSubTask = require("./resources/concourseInputSubTask.js"); 13 | 14 | nock.disableNetConnect(); 15 | 16 | describe("create or update issue", () => { 17 | beforeEach(() => { 18 | nock.cleanAll(); 19 | }); 20 | 21 | describe("create", () => { 22 | it("creates the jira issue", done => { 23 | let input = concourseInput(); 24 | let create = setupCreateTask({ 25 | fields: { 26 | project: { 27 | key: "ATP" 28 | }, 29 | issuetype: { 30 | name: "Bug" 31 | }, 32 | summary: "TEST 1.106.0", 33 | description: "Inline static description" 34 | } 35 | }); 36 | 37 | createIssue("", input.source, input.params, () => { 38 | expect(create.isDone()).to.be.true; 39 | done(); 40 | }); 41 | }); 42 | 43 | it("returns the new issue", done => { 44 | let input = concourseInput(); 45 | 46 | setupCreateTask({ 47 | fields: { 48 | project: { 49 | key: "ATP" 50 | }, 51 | issuetype: { 52 | name: "Bug" 53 | }, 54 | summary: "TEST 1.106.0", 55 | description: "Inline static description" 56 | } 57 | }); 58 | 59 | createIssue( 60 | "", 61 | input.source, 62 | input.params, 63 | (error, result) => { 64 | expect(error).to.be.null; 65 | expect(result).to.deep.equal([{ 66 | id: "15805", 67 | key: "ATP-1", 68 | self: "http://jira.comrest/api/2/issue/15805" 69 | }]); 70 | done(); 71 | } 72 | ); 73 | }); 74 | 75 | it("creates the jira sub task", done => { 76 | const input = concourseInputSubTask(); 77 | const create = setupCreateSubTask({ 78 | fields: { 79 | project: { 80 | key: "ATP" 81 | }, 82 | parent: { 83 | key: "ATP-1" 84 | }, 85 | issuetype: { 86 | name: "Sub: Task" 87 | }, 88 | summary: "Test sub task", 89 | description: "Inline static description for sub task" 90 | } 91 | }); 92 | 93 | createIssue("", input.source, input.params, () => { 94 | expect(create.isDone()).to.be.true; 95 | done(); 96 | }); 97 | }); 98 | 99 | it("handles an additional field", done => { 100 | let input = concourseInput(); 101 | 102 | input.params.fields.environment = "PROD"; 103 | 104 | let createWithEnv = setupCreateTask({ 105 | fields: { 106 | project: { 107 | key: "ATP" 108 | }, 109 | issuetype: { 110 | name: "Bug" 111 | }, 112 | summary: "TEST 1.106.0", 113 | description: "Inline static description", 114 | environment: "PROD" 115 | } 116 | }); 117 | 118 | createIssue("", input.source, input.params, () => { 119 | expect(createWithEnv.isDone()).to.be.true; 120 | done(); 121 | }); 122 | }); 123 | 124 | it("parses a $NOW", done => { 125 | let input = concourseInput(); 126 | 127 | input.params.fields.duedate = "$NOW"; 128 | 129 | let createWithDate = setupCreateTask(body => { 130 | let duedate = moment(body.fields.duedate); 131 | let now = moment(); 132 | 133 | return Math.abs(duedate.diff(now, "s")) < 1; 134 | }); 135 | 136 | createIssue("", input.source, input.params, () => { 137 | expect(createWithDate.isDone()).to.be.true; 138 | done(); 139 | }); 140 | }); 141 | 142 | it("parses a $NOWs with +/-", done => { 143 | let input = concourseInput(); 144 | 145 | input.params.fields.duedate = "$NOW+5"; 146 | input.params.fields.tomorrow = "$NOW+1d"; 147 | input.params.fields.abitago = "$NOW-8h"; 148 | 149 | let createWithDate = setupCreateTask(body => { 150 | let duedate = moment(body.fields.duedate); 151 | 152 | let expectedDueDate = moment().add(5, "m"); 153 | 154 | return Math.abs(expectedDueDate.diff(duedate, "s")) < 1; 155 | }); 156 | 157 | createIssue("", input.source, input.params, () => { 158 | expect(createWithDate.isDone()).to.be.true; 159 | done(); 160 | }); 161 | }); 162 | 163 | it("parses a $NOWs with +/- and units", done => { 164 | let input = concourseInput(); 165 | 166 | input.params.fields.tomorrow = "$NOW+1d"; 167 | input.params.fields.abitago = "$NOW-8h"; 168 | 169 | let createWithDate = setupCreateTask(body => { 170 | let tomorrow = moment(body.fields.tomorrow); 171 | let abitago = moment(body.fields.abitago); 172 | 173 | let expectedTomorrow = moment().add(1, "d"); 174 | let expectedAbitago = moment().add(-8, "h"); 175 | 176 | return ( 177 | Math.abs(expectedTomorrow.diff(tomorrow, "s")) < 1 && 178 | Math.abs(expectedAbitago.diff(abitago, "s")) < 1 179 | ); 180 | }); 181 | 182 | createIssue("", input.source, input.params, () => { 183 | expect(createWithDate.isDone()).to.be.true; 184 | done(); 185 | }); 186 | }); 187 | 188 | it("handles a custom field", done => { 189 | let input = concourseInput(); 190 | 191 | input.params.custom_fields = { 192 | something: { 193 | id: 10201, 194 | value: "dave!" 195 | } 196 | }; 197 | 198 | let createWithCustom = setupCreateTask({ 199 | fields: { 200 | project: { 201 | key: "ATP" 202 | }, 203 | issuetype: { 204 | name: "Bug" 205 | }, 206 | summary: "TEST 1.106.0", 207 | description: "Inline static description", 208 | customfield_10201: "dave!" 209 | } 210 | }); 211 | 212 | createIssue("", input.source, input.params, () => { 213 | expect(createWithCustom.isDone()).to.be.true; 214 | done(); 215 | }); 216 | }); 217 | 218 | it("handles a selectlist custom field with a string value", done => { 219 | let input = concourseInput(); 220 | 221 | input.params.custom_fields = { 222 | selectlist_field: { 223 | id: 10201, 224 | type: "selectlist", 225 | value: "dave!" 226 | } 227 | }; 228 | 229 | let createWithCustom = setupCreateTask({ 230 | fields: { 231 | project: { 232 | key: "ATP" 233 | }, 234 | issuetype: { 235 | name: "Bug" 236 | }, 237 | summary: "TEST 1.106.0", 238 | description: "Inline static description", 239 | customfield_10201: { value: "dave!" } 240 | } 241 | }); 242 | 243 | createIssue("", input.source, input.params, () => { 244 | expect(createWithCustom.isDone()).to.be.true; 245 | done(); 246 | }); 247 | }); 248 | 249 | it("handles a selectlist custom field with a value id", done => { 250 | let input = concourseInput(); 251 | 252 | input.params.custom_fields = { 253 | selectlist_field: { 254 | id: 10201, 255 | type: "selectlist", 256 | value_id: 123 257 | } 258 | }; 259 | 260 | let createWithCustom = setupCreateTask({ 261 | fields: { 262 | project: { 263 | key: "ATP" 264 | }, 265 | issuetype: { 266 | name: "Bug" 267 | }, 268 | summary: "TEST 1.106.0", 269 | description: "Inline static description", 270 | customfield_10201: { id: "123" } 271 | } 272 | }); 273 | 274 | createIssue("", input.source, input.params, () => { 275 | expect(createWithCustom.isDone()).to.be.true; 276 | done(); 277 | }); 278 | }); 279 | 280 | it("handles multiple custom fields", done => { 281 | let input = concourseInput(); 282 | 283 | input.params.custom_fields = { 284 | something: { 285 | id: 10201, 286 | value: "dave!" 287 | }, 288 | somethingElse: { 289 | id: 12345, 290 | value: "meh" 291 | } 292 | }; 293 | 294 | let createWithCustom = setupCreateTask({ 295 | fields: { 296 | project: { 297 | key: "ATP" 298 | }, 299 | issuetype: { 300 | name: "Bug" 301 | }, 302 | summary: "TEST 1.106.0", 303 | description: "Inline static description", 304 | customfield_10201: "dave!", 305 | customfield_12345: "meh" 306 | } 307 | }); 308 | 309 | createIssue("", input.source, input.params, () => { 310 | expect(createWithCustom.isDone()).to.be.true; 311 | done(); 312 | }); 313 | }); 314 | 315 | it("handles an error response", done => { 316 | let input = concourseInput(); 317 | 318 | nock(jira.url) 319 | .post("/rest/api/2/issue/", { 320 | fields: { 321 | project: { 322 | key: "ATP" 323 | }, 324 | issuetype: { 325 | name: "Bug" 326 | }, 327 | summary: "TEST 1.106.0", 328 | description: "Inline static description" 329 | } 330 | }) 331 | .basicAuth({ 332 | user: jira.user, 333 | pass: jira.token 334 | }) 335 | .reply(400, { 336 | errorMessages: [], 337 | errors: { 338 | environment: "Environment is required.", 339 | duedate: "Due Date is required." 340 | } 341 | }); 342 | 343 | createIssue( 344 | "", 345 | input.source, 346 | input.params, 347 | (error, result) => { 348 | expect(error.message).to.equal("Could not update Jira."); 349 | expect(result).to.be.undefined; 350 | done(); 351 | } 352 | ); 353 | }); 354 | 355 | it("handles an error in the request", done => { 356 | nock.cleanAll(); 357 | 358 | const input = concourseInput(); 359 | 360 | createIssue( 361 | "", 362 | input.source, 363 | input.params, 364 | (error, result) => { 365 | expect(error).to.not.be.null; 366 | expect(error.message).to.not.equal( 367 | "Could not update Jira." 368 | ); 369 | expect(result).to.be.undefined; 370 | done(); 371 | } 372 | ); 373 | }); 374 | 375 | it("handles an integer value", done => { 376 | let input = concourseInput(); 377 | input.params.fields.build_number = 12345; 378 | 379 | let create = setupCreateTask({ 380 | fields: { 381 | project: { 382 | key: "ATP" 383 | }, 384 | issuetype: { 385 | name: "Bug" 386 | }, 387 | summary: "TEST 1.106.0", 388 | description: "Inline static description", 389 | build_number: "12345" 390 | } 391 | }); 392 | 393 | createIssue("", input.source, input.params, () => { 394 | expect(create.isDone()).to.be.true; 395 | done(); 396 | }); 397 | }); 398 | }); 399 | 400 | describe("update", () => { 401 | let update; 402 | let issueId = 15805; 403 | 404 | let issue = { 405 | expand: 406 | "operations,versionedRepresentations,editmeta,changelog,renderedFields", 407 | id: issueId, 408 | self: jira.url + "/rest/api/2/issue/" + issueId, 409 | key: "ATP-1", 410 | fields: { 411 | summary: "TEST 1.106.0" 412 | } 413 | }; 414 | 415 | beforeEach(() => { 416 | update = nock(jira.url) 417 | .put("/rest/api/2/issue/" + issueId, { 418 | fields: { 419 | project: { 420 | key: "ATP" 421 | }, 422 | issuetype: { 423 | name: "Bug" 424 | }, 425 | summary: "TEST 1.106.0", 426 | description: "Inline static description" 427 | } 428 | }) 429 | .basicAuth({ 430 | user: jira.user, 431 | pass: jira.token 432 | }) 433 | .reply(201); 434 | }); 435 | 436 | it("updates the issue", done => { 437 | let input = concourseInput(); 438 | 439 | updateIssue("", issue, input.source, input.params, () => { 440 | expect(update.isDone()).to.be.true; 441 | done(); 442 | }); 443 | }); 444 | 445 | it("returns the issue", done => { 446 | let input = concourseInput(); 447 | 448 | updateIssue( 449 | "", 450 | issue, 451 | input.source, 452 | input.params, 453 | (error, result) => { 454 | expect(error).to.be.null; 455 | expect(result).to.deep.equal({ 456 | expand: 457 | "operations,versionedRepresentations,editmeta,changelog,renderedFields", 458 | id: issueId, 459 | self: jira.url + "/rest/api/2/issue/" + issueId, 460 | key: "ATP-1", 461 | fields: { 462 | summary: "TEST 1.106.0" 463 | } 464 | }); 465 | done(); 466 | } 467 | ); 468 | }); 469 | 470 | it("handles an error response", done => { 471 | nock.cleanAll(); 472 | 473 | let input = concourseInput(); 474 | 475 | nock(jira.url) 476 | .put("/rest/api/2/issue/" + issueId, { 477 | fields: { 478 | project: { 479 | key: "ATP" 480 | }, 481 | issuetype: { 482 | name: "Bug" 483 | }, 484 | summary: "TEST 1.106.0", 485 | description: "Inline static description" 486 | } 487 | }) 488 | .basicAuth({ 489 | user: jira.user, 490 | pass: jira.token 491 | }) 492 | .reply(400, { 493 | errorMessages: [], 494 | errors: { 495 | environment: "Environment is required.", 496 | duedate: "Due Date is required." 497 | } 498 | }); 499 | 500 | updateIssue( 501 | "", 502 | issue, 503 | input.source, 504 | input.params, 505 | error => { 506 | expect(error.message).to.equal("Could not update Jira."); 507 | done(); 508 | } 509 | ); 510 | }); 511 | 512 | it("handles an error in the request", done => { 513 | nock.cleanAll(); 514 | 515 | let input = concourseInput(); 516 | 517 | updateIssue( 518 | "", 519 | issue, 520 | input.source, 521 | input.params, 522 | error => { 523 | expect(error).to.not.be.null; 524 | expect(error.message).to.not.equal( 525 | "Could not update Jira." 526 | ); 527 | done(); 528 | } 529 | ); 530 | }); 531 | 532 | it("can update an issue using just a summary", done => { 533 | let input = { 534 | params: { 535 | summary: "TEST 1.106.0" 536 | }, 537 | source: { 538 | url: jira.url, 539 | email: jira.user, 540 | apitoken: jira.token, 541 | project: "ATP" 542 | } 543 | }; 544 | 545 | let updateWithOnlySummary = nock(jira.url) 546 | .put("/rest/api/2/issue/" + issueId, { 547 | fields: { 548 | summary: "TEST 1.106.0", 549 | project: { 550 | key: "ATP" 551 | } 552 | } 553 | }) 554 | .basicAuth({ 555 | user: jira.user, 556 | pass: jira.token 557 | }) 558 | .reply(201); 559 | 560 | updateIssue("", issue, input.source, input.params, () => { 561 | expect(updateWithOnlySummary.isDone()).to.be.true; 562 | done(); 563 | }); 564 | }); 565 | }); 566 | 567 | describe("files", () => { 568 | let dir = process.cwd() + "/spec"; 569 | 570 | it("can use a file for text", done => { 571 | let input = concourseInput(); 572 | input.params.fields.description = { 573 | file: "resources/sample.out" 574 | }; 575 | 576 | let create = setupCreateTask({ 577 | fields: { 578 | project: { 579 | key: "ATP" 580 | }, 581 | issuetype: { 582 | name: "Bug" 583 | }, 584 | summary: "TEST 1.106.0", 585 | description: "Text from file" 586 | } 587 | }); 588 | 589 | createIssue(dir, input.source, input.params, () => { 590 | expect(create.isDone()).to.be.true; 591 | done(); 592 | }); 593 | }); 594 | 595 | it("replaces $FILE with contents of file", done => { 596 | let input = concourseInput(); 597 | input.params.summary = { 598 | text: "Summary - $FILE", 599 | file: "resources/sample.out" 600 | }; 601 | input.params.fields.description = { 602 | text: "Static text - $FILE", 603 | file: "resources/sample.out" 604 | }; 605 | 606 | let create = setupCreateTask({ 607 | fields: { 608 | project: { 609 | key: "ATP" 610 | }, 611 | issuetype: { 612 | name: "Bug" 613 | }, 614 | summary: "Summary - Text from file", 615 | description: "Static text - Text from file" 616 | } 617 | }); 618 | 619 | createIssue(dir, input.source, input.params, () => { 620 | expect(create.isDone()).to.be.true; 621 | done(); 622 | }); 623 | }); 624 | }); 625 | }); 626 | 627 | function setupCreateTask(expectedBody) { 628 | return nock(jira.url) 629 | .post("/rest/api/2/issue/", expectedBody) 630 | .basicAuth({ 631 | user: jira.user, 632 | pass: jira.token 633 | }) 634 | .reply(200, { 635 | id: "15805", 636 | key: "ATP-1", 637 | self: jira.url + "rest/api/2/issue/15805" 638 | }); 639 | } 640 | 641 | function setupCreateSubTask(expectedBody) { 642 | return nock(jira.url) 643 | .post("/rest/api/2/issue/", expectedBody) 644 | .basicAuth({ 645 | user: jira.user, 646 | pass: jira.token 647 | }) 648 | .reply(200, { 649 | id: "15806", 650 | key: "ATP-2", 651 | self: jira.url + "rest/api/2/issue/15806" 652 | }); 653 | } 654 | -------------------------------------------------------------------------------- /spec/out.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const expect = chai.expect; 3 | 4 | const nock = require("nock"); 5 | const out = require("../src/out.js"); 6 | 7 | const jira = require("./resources/jiraDetails.js"); 8 | 9 | const baseFileDir = process.cwd() + "/spec"; 10 | 11 | /** Uncomment the below for sexy debug shizzles **/ 12 | //require("request-debug")(require("request")); 13 | 14 | nock.disableNetConnect(); 15 | 16 | describe("jira resource", () => { 17 | let issueId = "12345"; 18 | let summary = "TEST - 1.0.1"; 19 | 20 | beforeEach(() => { 21 | nock.cleanAll(); 22 | }); 23 | 24 | it("accepts legacy basic auth credentials", done => { 25 | setupSearch(); 26 | 27 | const create = setupCreate(); 28 | const watchers = setupAddWatchers(); 29 | const transitions = setupTransitions(); 30 | 31 | let input = getInput(); 32 | delete input.source.email; 33 | delete input.source.apitoken; 34 | input.source.username = jira.user; 35 | input.source.password = jira.token; 36 | 37 | out(input, baseFileDir, (error) => { 38 | expect(error).to.be.null; 39 | done(); 40 | }); 41 | }); 42 | 43 | it("can create a ticket", done => { 44 | setupSearch(); 45 | 46 | let create = setupCreate(); 47 | let watchers = setupAddWatchers(); 48 | let transitions = setupTransitions(); 49 | 50 | let input = getInput(); 51 | 52 | out(input, baseFileDir, (error, result) => { 53 | expect(error).to.be.null; 54 | expect(result).to.deep.equal({ 55 | version: { ref: "none" }, 56 | metadata: [{ name: "ATP-51", "value": "http://jira.com/secure/QuickSearch.jspa?searchString=ATP-51"}] 57 | }); 58 | 59 | expect(create.isDone(), "create ticket").to.be.true; 60 | expect(watchers.isDone(), "add watchers").to.be.true; 61 | expect(transitions.isDone(), "perform transitions").to.be.true; 62 | 63 | done(); 64 | }); 65 | }); 66 | 67 | it("can update a ticket", done => { 68 | setupSearch({ 69 | expand: 70 | "operations,versionedRepresentations,editmeta,changelog,renderedFields", 71 | id: issueId, 72 | self: jira.url + "/rest/api/2/issue/" + issueId, 73 | key: "ATP-1", 74 | fields: { 75 | summary: "TEST 1.106.0" 76 | } 77 | }); 78 | 79 | let update = setupUpdate(); 80 | let watchers = setupAddWatchers(); 81 | let transitions = setupTransitions(); 82 | 83 | let input = getInput(); 84 | 85 | out(input, baseFileDir, (error, result) => { 86 | expect(error).to.be.null; 87 | expect(result).to.deep.equal({ 88 | version: { ref: "none" }, 89 | metadata: [{ name: "ATP-1", "value": "http://jira.com/secure/QuickSearch.jspa?searchString=ATP-1"}] 90 | }); 91 | 92 | expect(update.isDone(), "create ticket").to.be.true; 93 | expect(watchers.isDone(), "add watchers").to.be.true; 94 | expect(transitions.isDone(), "perform transitions").to.be.true; 95 | 96 | done(); 97 | }); 98 | }); 99 | 100 | function getInput() { 101 | return { 102 | params: { 103 | issue_type: "Feature", 104 | summary: { 105 | file: "resources/sample.version", 106 | text: "TEST - $FILE" 107 | }, 108 | fields: { 109 | description: { 110 | file: "resources/sample.out", 111 | text: "Sample description [$FILE]" 112 | }, 113 | environment: "Prod" 114 | }, 115 | custom_fields: { 116 | a_custom_field: { 117 | id: 10201, 118 | value: 12345 119 | }, 120 | another_one: { 121 | id: 76552, 122 | value: "something" 123 | } 124 | }, 125 | transitions: ["Review", "Done"], 126 | watchers: ["dave", "amier"] 127 | }, 128 | source: { 129 | url: jira.url, 130 | email: jira.user, 131 | apitoken: jira.token, 132 | project: "ATP" 133 | } 134 | }; 135 | } 136 | 137 | function setupSearch(issue) { 138 | let issues = issue ? [issue] : []; 139 | 140 | nock(jira.url) 141 | .post("/rest/api/2/search/", { 142 | jql: 143 | 'project="ATP" AND summary~"' + 144 | summary + 145 | '" ORDER BY id DESC', 146 | maxResults: 1, 147 | fields: ["key", "summary"] 148 | }) 149 | .basicAuth({ 150 | user: jira.user, 151 | pass: jira.token 152 | }) 153 | .reply(200, { 154 | expand: "names,schema", 155 | startAt: 0, 156 | maxResults: 1, 157 | total: issues.length, 158 | issues: issues 159 | }); 160 | } 161 | 162 | function setupCreate() { 163 | return nock(jira.url) 164 | .post("/rest/api/2/issue/", { 165 | fields: { 166 | project: { 167 | key: "ATP" 168 | }, 169 | issuetype: { 170 | name: "Feature" 171 | }, 172 | summary: summary, 173 | description: "Sample description [Text from file]", 174 | environment: "Prod", 175 | customfield_10201: "12345", 176 | customfield_76552: "something" 177 | } 178 | }) 179 | .basicAuth({ 180 | user: jira.user, 181 | pass: jira.token 182 | }) 183 | .reply(200, { 184 | id: issueId, 185 | key: "ATP-51", 186 | self: jira.url + "/rest/api/2/issue/" + issueId 187 | }); 188 | } 189 | 190 | function setupUpdate() { 191 | return nock(jira.url) 192 | .put("/rest/api/2/issue/" + issueId, { 193 | fields: { 194 | project: { 195 | key: "ATP" 196 | }, 197 | issuetype: { 198 | name: "Feature" 199 | }, 200 | summary: summary, 201 | description: "Sample description [Text from file]", 202 | environment: "Prod", 203 | customfield_10201: "12345", 204 | customfield_76552: "something" 205 | } 206 | }) 207 | .basicAuth({ 208 | user: jira.user, 209 | pass: jira.token 210 | }) 211 | .reply(201); 212 | } 213 | 214 | function setupAddWatchers() { 215 | return nock(jira.url) 216 | .post("/rest/api/2/issue/" + issueId + "/watchers/", '"dave"') 217 | .basicAuth({ 218 | user: jira.user, 219 | pass: jira.token 220 | }) 221 | .reply(204) 222 | .post("/rest/api/2/issue/" + issueId + "/watchers/", '"amier"') 223 | .basicAuth({ 224 | user: jira.user, 225 | pass: jira.token 226 | }) 227 | .reply(204); 228 | } 229 | 230 | function setupTransitions() { 231 | nock(jira.url) 232 | .get("/rest/api/2/issue/" + issueId + "/transitions/") 233 | .basicAuth({ 234 | user: jira.user, 235 | pass: jira.token 236 | }) 237 | .reply(200, { 238 | expand: "transitions", 239 | transitions: [ 240 | { 241 | id: "321", 242 | name: "Review" 243 | } 244 | ] 245 | }) 246 | .get("/rest/api/2/issue/" + issueId + "/transitions/") 247 | .basicAuth({ 248 | user: jira.user, 249 | pass: jira.token 250 | }) 251 | .reply(200, { 252 | expand: "transitions", 253 | transitions: [ 254 | { 255 | id: "456", 256 | name: "Done" 257 | }, 258 | { 259 | id: "789", 260 | name: "Reject" 261 | } 262 | ] 263 | }); 264 | 265 | return nock(jira.url) 266 | .post("/rest/api/2/issue/" + issueId + "/transitions/", { 267 | transition: { 268 | id: "321" 269 | } 270 | }) 271 | .basicAuth({ 272 | user: jira.user, 273 | pass: jira.token 274 | }) 275 | .reply(204) 276 | .post("/rest/api/2/issue/" + issueId + "/transitions/", { 277 | transition: { 278 | id: "456" 279 | } 280 | }) 281 | .basicAuth({ 282 | user: jira.user, 283 | pass: jira.token 284 | }) 285 | .reply(204); 286 | } 287 | }); 288 | -------------------------------------------------------------------------------- /spec/processTransitions.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const expect = chai.expect; 3 | 4 | const nock = require("nock"); 5 | 6 | const processTransitions = require("../src/processTransitions.js"); 7 | 8 | const jira = require("./resources/jiraDetails.js"); 9 | const concourseInput = require("./resources/concourseInput.js"); 10 | 11 | nock.disableNetConnect(); 12 | 13 | describe("processTransitions", () => { 14 | let issue = { 15 | id: "15805", 16 | key: "ATP-1", 17 | self: jira.url + "rest/api/2/issue/15805" 18 | }; 19 | 20 | beforeEach(() => { 21 | nock.cleanAll(); 22 | }); 23 | 24 | it("updates an issue with a transition", done => { 25 | nock(jira.url) 26 | .get("/rest/api/2/issue/" + issue.id + "/transitions/") 27 | .basicAuth({ 28 | user: jira.user, 29 | pass: jira.token 30 | }) 31 | .reply(200, { 32 | expand: "transitions", 33 | transitions: [ 34 | { 35 | id: "51", 36 | name: "Submit" 37 | } 38 | ] 39 | }); 40 | 41 | let transition = nock(jira.url) 42 | .post("/rest/api/2/issue/" + issue.id + "/transitions/", { 43 | transition: { 44 | id: "51" 45 | } 46 | }) 47 | .basicAuth({ 48 | user: jira.user, 49 | pass: jira.token 50 | }) 51 | .reply(204); 52 | 53 | let input = concourseInput(); 54 | input.params.transitions = ["Submit"]; 55 | 56 | processTransitions(issue, input.source, input.params, () => { 57 | expect(transition.isDone()).to.be.true; 58 | done(); 59 | }); 60 | }); 61 | 62 | it("can handle multiple possible transitions", done => { 63 | let input = concourseInput(); 64 | input.params.transitions = ["Reject"]; 65 | 66 | nock(jira.url) 67 | .get("/rest/api/2/issue/" + issue.id + "/transitions/") 68 | .basicAuth({ 69 | user: jira.user, 70 | pass: jira.token 71 | }) 72 | .reply(200, { 73 | expand: "transitions", 74 | transitions: [ 75 | { 76 | id: "51", 77 | name: "Submit" 78 | }, 79 | { 80 | id: "60", 81 | name: "Reject" 82 | } 83 | ] 84 | }); 85 | 86 | let transition = nock(jira.url) 87 | .post("/rest/api/2/issue/" + issue.id + "/transitions/", { 88 | transition: { 89 | id: "60" 90 | } 91 | }) 92 | .basicAuth({ 93 | user: jira.user, 94 | pass: jira.token 95 | }) 96 | .reply(204); 97 | 98 | processTransitions(issue, input.source, input.params, () => { 99 | expect(transition.isDone()).to.be.true; 100 | done(); 101 | }); 102 | }); 103 | 104 | it("can progress through multiple transitions", done => { 105 | let input = concourseInput(); 106 | input.params.transitions = ["Submit", "Test", "Reject"]; 107 | 108 | nock(jira.url) 109 | .get("/rest/api/2/issue/" + issue.id + "/transitions/") 110 | .basicAuth({ user: jira.user, pass: jira.token }) 111 | .reply(200, { 112 | expand: "transitions", 113 | transitions: [{ id: "51", name: "Submit" }] 114 | }) 115 | .get("/rest/api/2/issue/" + issue.id + "/transitions/") 116 | .basicAuth({ user: jira.user, pass: jira.token }) 117 | .reply(200, { 118 | expand: "transitions", 119 | transitions: [{ id: "69", name: "Test" }] 120 | }) 121 | .get("/rest/api/2/issue/" + issue.id + "/transitions/") 122 | .basicAuth({ user: jira.user, pass: jira.token }) 123 | .reply(200, { 124 | expand: "transitions", 125 | transitions: [ 126 | { id: "12", name: "Complete" }, 127 | { id: "13", name: "Reject" } 128 | ] 129 | }); 130 | 131 | let transitions = nock(jira.url) 132 | .post("/rest/api/2/issue/" + issue.id + "/transitions/", { 133 | transition: { 134 | id: "51" 135 | } 136 | }) 137 | .basicAuth({ user: jira.user, pass: jira.token }) 138 | .reply(204) 139 | .post("/rest/api/2/issue/" + issue.id + "/transitions/", { 140 | transition: { 141 | id: "69" 142 | } 143 | }) 144 | .basicAuth({ user: jira.user, pass: jira.token }) 145 | .reply(204) 146 | .post("/rest/api/2/issue/" + issue.id + "/transitions/", { 147 | transition: { 148 | id: "13" 149 | } 150 | }) 151 | .basicAuth({ user: jira.user, pass: jira.token }) 152 | .reply(204); 153 | 154 | processTransitions(issue, input.source, input.params, () => { 155 | expect(transitions.isDone()).to.be.true; 156 | done(); 157 | }); 158 | }); 159 | 160 | it("isn't case sensitive for transitions", done => { 161 | nock(jira.url) 162 | .get("/rest/api/2/issue/" + issue.id + "/transitions/") 163 | .basicAuth({ 164 | user: jira.user, 165 | pass: jira.token 166 | }) 167 | .reply(200, { 168 | expand: "transitions", 169 | transitions: [ 170 | { 171 | id: "51", 172 | name: "SUBMIT" 173 | } 174 | ] 175 | }); 176 | 177 | let transition = nock(jira.url) 178 | .post("/rest/api/2/issue/" + issue.id + "/transitions/", { 179 | transition: { 180 | id: "51" 181 | } 182 | }) 183 | .basicAuth({ 184 | user: jira.user, 185 | pass: jira.token 186 | }) 187 | .reply(204); 188 | 189 | let input = concourseInput(); 190 | 191 | input.params.transitions = ["submit"]; 192 | 193 | processTransitions(issue, input.source, input.params, () => { 194 | expect(transition.isDone()).to.be.true; 195 | done(); 196 | }); 197 | }); 198 | }); 199 | -------------------------------------------------------------------------------- /spec/resources/concourseInput.js: -------------------------------------------------------------------------------- 1 | const jira = require('./jiraDetails.js') 2 | 3 | module.exports = () => { 4 | return { 5 | params: { 6 | issue_type: 'Bug', 7 | summary: 'TEST 1.106.0', 8 | fields: { 9 | description: 'Inline static description' 10 | } 11 | }, 12 | source: { 13 | url: jira.url, 14 | email: jira.user, 15 | apitoken: jira.token, 16 | project: 'ATP' 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /spec/resources/concourseInputSubTask.js: -------------------------------------------------------------------------------- 1 | const jira = require('./jiraDetails.js') 2 | 3 | module.exports = () => { 4 | return { 5 | params: { 6 | parent: 'ATP-1', 7 | issue_type: 'Sub: Task', 8 | summary: 'Test sub task', 9 | fields: { 10 | description: 'Inline static description for sub task' 11 | } 12 | }, 13 | source: { 14 | url: jira.url, 15 | email: jira.user, 16 | apitoken: jira.token, 17 | project: 'ATP' 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/resources/jira-resource.postman_collection.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "name": "jira-resource", 4 | "_postman_id": "1bfeadff-bda3-b286-9b86-fb8e9e6887b1", 5 | "description": "JIRA REST API examples\nThis guide contains different examples of how to use the JIRA REST API, including how to query issues, create an issue, edit an issue, etc. The reference documentation for the JIRA Server platform REST API is here: JIRA Server platform REST API. If you’ve never used the JIRA REST APIs before, we recommend that you also read the overview: About the JIRA REST APIs.\n\nThe examples on this page use curl. If an input file is required, it is denoted by the --data @filename syntax and the file data is shown separately.", 6 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 7 | }, 8 | "item": [ 9 | { 10 | "name": "Create task", 11 | "request": { 12 | "auth": { 13 | "type": "basic", 14 | "basic": [ 15 | { 16 | "key": "username", 17 | "value": "{{user}}", 18 | "type": "string" 19 | }, 20 | { 21 | "key": "password", 22 | "value": "{{pass}}", 23 | "type": "string" 24 | }, 25 | { 26 | "key": "saveHelperData", 27 | "value": true, 28 | "type": "boolean" 29 | }, 30 | { 31 | "key": "showPassword", 32 | "value": false, 33 | "type": "boolean" 34 | } 35 | ] 36 | }, 37 | "method": "POST", 38 | "header": [ 39 | { 40 | "key": "Content-Type", 41 | "value": "application/json" 42 | }, 43 | { 44 | "key": "Authorization", 45 | "value": "Basic YW5kcmUubGFkZW1hbm46QWhhc2l0YWcxMw==" 46 | } 47 | ], 48 | "body": { 49 | "mode": "raw", 50 | "raw": "{\n \"fields\": {\n \"project\":\n { \n \"key\": \"{{project}}\"\n },\n \"summary\": \"REST ye merry gentlemen.\",\n \"description\": \"Creating of an issue using project keys and issue type names using the REST API\",\n \"issuetype\": {\n \"name\": \"Task\"\n }\n }\n}" 51 | }, 52 | "url": { 53 | "raw": "{{url}}/rest/api/2/issue/", 54 | "host": [ 55 | "{{url}}" 56 | ], 57 | "path": [ 58 | "rest", 59 | "api", 60 | "2", 61 | "issue", 62 | "" 63 | ] 64 | }, 65 | "description": "Creating an issue using the JIRA REST API is as simple as making a POST with a JSON document. To create an issue, you will need to know certain key metadata, like the ID of the project that the issue will be created in, or the ID of the issue type. You can request the create metadata for all issue types across all projects by using the createmeta resource. For example:" 66 | }, 67 | "response": [] 68 | }, 69 | { 70 | "name": "Create sub task", 71 | "request": { 72 | "auth": { 73 | "type": "basic", 74 | "basic": [ 75 | { 76 | "key": "username", 77 | "value": "{{user}}", 78 | "type": "string" 79 | }, 80 | { 81 | "key": "password", 82 | "value": "{{pass}}", 83 | "type": "string" 84 | }, 85 | { 86 | "key": "saveHelperData", 87 | "value": true, 88 | "type": "boolean" 89 | }, 90 | { 91 | "key": "showPassword", 92 | "value": false, 93 | "type": "boolean" 94 | } 95 | ] 96 | }, 97 | "method": "POST", 98 | "header": [ 99 | { 100 | "key": "Content-Type", 101 | "value": "application/json" 102 | }, 103 | { 104 | "key": "Authorization", 105 | "value": "Basic YW5kcmUubGFkZW1hbm46QWhhc2l0YWcxMw==" 106 | } 107 | ], 108 | "body": { 109 | "mode": "raw", 110 | "raw": "{\n \"fields\": {\n \"project\":\n { \n \"key\": \"{{project}}\"\n },\n \"parent\":\n { \n \"key\": \"{{issue}}\"\n },\n \"summary\": \"Another subtask.\",\n \"description\": \"Creating of an issue using project keys and issue type names using the REST API\",\n \"issuetype\": {\n \"name\": \"Sub: Task\"\n }\n }\n}" 111 | }, 112 | "url": { 113 | "raw": "{{url}}/rest/api/2/issue/", 114 | "host": [ 115 | "{{url}}" 116 | ], 117 | "path": [ 118 | "rest", 119 | "api", 120 | "2", 121 | "issue", 122 | "" 123 | ] 124 | }, 125 | "description": "Creating an issue using the JIRA REST API is as simple as making a POST with a JSON document. To create an issue, you will need to know certain key metadata, like the ID of the project that the issue will be created in, or the ID of the issue type. You can request the create metadata for all issue types across all projects by using the createmeta resource. For example:" 126 | }, 127 | "response": [] 128 | }, 129 | { 130 | "name": "Adding comment", 131 | "request": { 132 | "auth": { 133 | "type": "basic", 134 | "basic": [ 135 | { 136 | "key": "username", 137 | "value": "{{user}}", 138 | "type": "string" 139 | }, 140 | { 141 | "key": "password", 142 | "value": "{{pass}}", 143 | "type": "string" 144 | }, 145 | { 146 | "key": "saveHelperData", 147 | "value": true, 148 | "type": "boolean" 149 | }, 150 | { 151 | "key": "showPassword", 152 | "value": false, 153 | "type": "boolean" 154 | } 155 | ] 156 | }, 157 | "method": "POST", 158 | "header": [ 159 | { 160 | "key": "Content-Type", 161 | "value": "application/json" 162 | } 163 | ], 164 | "body": { 165 | "mode": "raw", 166 | "raw": "{\n \"body\": \"h2. Example comment\\nLi Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles. Ma quande lingues coalesce, li grammatica del resultant lingue es plu simplic e regulari quam ti del coalescent lingues. Li nov lingua franca va esser plu simplic e regulari quam li existent Europan lingues. It va esser tam simplic quam Occidental in fact, it va esser Occidental. A un Angleso it va semblar un simplificat Angles, quam un skeptic Cambridge amico dit me que Occidental es.Li Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles.\"\n}" 167 | }, 168 | "url": { 169 | "raw": "{{url}}/rest/api/2/issue/{{issue}}/comment", 170 | "host": [ 171 | "{{url}}" 172 | ], 173 | "path": [ 174 | "rest", 175 | "api", 176 | "2", 177 | "issue", 178 | "{{issue}}", 179 | "comment" 180 | ] 181 | }, 182 | "description": "" 183 | }, 184 | "response": [ 185 | { 186 | "id": "edc41f8a-99ac-47a7-a6a0-d4db00f36093", 187 | "name": "Adding comment", 188 | "originalRequest": { 189 | "method": "POST", 190 | "header": [ 191 | { 192 | "key": "Content-Type", 193 | "value": "application/json" 194 | }, 195 | { 196 | "key": "Authorization", 197 | "type": "text", 198 | "name": "Authorization", 199 | "value": "Basic YW5kcmUubGFkZW1hbm46QWhhc2l0YWcxMw==" 200 | } 201 | ], 202 | "body": { 203 | "mode": "raw", 204 | "raw": "{\n \"body\": \"h2. Example comment\\nLi Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles. Ma quande lingues coalesce, li grammatica del resultant lingue es plu simplic e regulari quam ti del coalescent lingues. Li nov lingua franca va esser plu simplic e regulari quam li existent Europan lingues. It va esser tam simplic quam Occidental in fact, it va esser Occidental. A un Angleso it va semblar un simplificat Angles, quam un skeptic Cambridge amico dit me que Occidental es.Li Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles.\"\n}" 205 | }, 206 | "url": { 207 | "raw": "{{url}}/rest/api/2/issue/{{issue}}/comment", 208 | "host": [ 209 | "{{url}}" 210 | ], 211 | "path": [ 212 | "rest", 213 | "api", 214 | "2", 215 | "issue", 216 | "{{issue}}", 217 | "comment" 218 | ] 219 | } 220 | }, 221 | "status": "Created", 222 | "code": 201, 223 | "_postman_previewlanguage": "json", 224 | "header": [ 225 | { 226 | "name": "cache-control", 227 | "key": "cache-control", 228 | "value": "no-cache, no-store, no-transform", 229 | "description": "" 230 | }, 231 | { 232 | "name": "connection", 233 | "key": "connection", 234 | "value": "Keep-Alive", 235 | "description": "" 236 | }, 237 | { 238 | "name": "content-encoding", 239 | "key": "content-encoding", 240 | "value": "gzip", 241 | "description": "" 242 | }, 243 | { 244 | "name": "content-security-policy", 245 | "key": "content-security-policy", 246 | "value": "frame-ancestors 'self'", 247 | "description": "" 248 | }, 249 | { 250 | "name": "content-type", 251 | "key": "content-type", 252 | "value": "application/json;charset=UTF-8", 253 | "description": "" 254 | }, 255 | { 256 | "name": "date", 257 | "key": "date", 258 | "value": "Tue, 22 May 2018 16:59:12 GMT", 259 | "description": "" 260 | }, 261 | { 262 | "name": "keep-alive", 263 | "key": "keep-alive", 264 | "value": "timeout=5, max=100", 265 | "description": "" 266 | }, 267 | { 268 | "name": "location", 269 | "key": "location", 270 | "value": "https://jira.netresearch.de/rest/api/2/issue/81370/comment/298321", 271 | "description": "" 272 | }, 273 | { 274 | "name": "transfer-encoding", 275 | "key": "transfer-encoding", 276 | "value": "chunked", 277 | "description": "" 278 | }, 279 | { 280 | "name": "vary", 281 | "key": "vary", 282 | "value": "User-Agent", 283 | "description": "" 284 | }, 285 | { 286 | "name": "x-arequestid", 287 | "key": "x-arequestid", 288 | "value": "1139x315518x1", 289 | "description": "" 290 | }, 291 | { 292 | "name": "x-asen", 293 | "key": "x-asen", 294 | "value": "SEN-10752348", 295 | "description": "" 296 | }, 297 | { 298 | "name": "x-asessionid", 299 | "key": "x-asessionid", 300 | "value": "1kzqi5l", 301 | "description": "" 302 | }, 303 | { 304 | "name": "x-ausername", 305 | "key": "x-ausername", 306 | "value": "andre.lademann", 307 | "description": "" 308 | }, 309 | { 310 | "name": "x-content-type-options", 311 | "key": "x-content-type-options", 312 | "value": "nosniff", 313 | "description": "" 314 | }, 315 | { 316 | "name": "x-frame-options", 317 | "key": "x-frame-options", 318 | "value": "SAMEORIGIN", 319 | "description": "" 320 | }, 321 | { 322 | "name": "x-seraph-loginreason", 323 | "key": "x-seraph-loginreason", 324 | "value": "OK", 325 | "description": "" 326 | }, 327 | { 328 | "name": "x-xss-protection", 329 | "key": "x-xss-protection", 330 | "value": "1; mode=block", 331 | "description": "" 332 | } 333 | ], 334 | "cookie": [], 335 | "responseTime": "273", 336 | "body": "{\"self\":\"https://jira.netresearch.de/rest/api/2/issue/81370/comment/298321\",\"id\":\"298321\",\"author\":{\"self\":\"https://jira.netresearch.de/rest/api/2/user?username=andre.lademann\",\"name\":\"andre.lademann\",\"key\":\"andre.lademann\",\"emailAddress\":\"andre.lademann@netresearch.de\",\"avatarUrls\":{\"48x48\":\"https://jira.netresearch.de/secure/useravatar?ownerId=andre.lademann&avatarId=15442\",\"24x24\":\"https://jira.netresearch.de/secure/useravatar?size=small&ownerId=andre.lademann&avatarId=15442\",\"16x16\":\"https://jira.netresearch.de/secure/useravatar?size=xsmall&ownerId=andre.lademann&avatarId=15442\",\"32x32\":\"https://jira.netresearch.de/secure/useravatar?size=medium&ownerId=andre.lademann&avatarId=15442\"},\"displayName\":\"André Lademann\",\"active\":true,\"timeZone\":\"Europe/Berlin\"},\"body\":\"h2. Example comment\\nLi Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles. Ma quande lingues coalesce, li grammatica del resultant lingue es plu simplic e regulari quam ti del coalescent lingues. Li nov lingua franca va esser plu simplic e regulari quam li existent Europan lingues. It va esser tam simplic quam Occidental in fact, it va esser Occidental. A un Angleso it va semblar un simplificat Angles, quam un skeptic Cambridge amico dit me que Occidental es.Li Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles.\",\"updateAuthor\":{\"self\":\"https://jira.netresearch.de/rest/api/2/user?username=andre.lademann\",\"name\":\"andre.lademann\",\"key\":\"andre.lademann\",\"emailAddress\":\"andre.lademann@netresearch.de\",\"avatarUrls\":{\"48x48\":\"https://jira.netresearch.de/secure/useravatar?ownerId=andre.lademann&avatarId=15442\",\"24x24\":\"https://jira.netresearch.de/secure/useravatar?size=small&ownerId=andre.lademann&avatarId=15442\",\"16x16\":\"https://jira.netresearch.de/secure/useravatar?size=xsmall&ownerId=andre.lademann&avatarId=15442\",\"32x32\":\"https://jira.netresearch.de/secure/useravatar?size=medium&ownerId=andre.lademann&avatarId=15442\"},\"displayName\":\"André Lademann\",\"active\":true,\"timeZone\":\"Europe/Berlin\"},\"created\":\"2018-05-22T18:59:12.397+0200\",\"updated\":\"2018-05-22T18:59:12.397+0200\"}" 337 | } 338 | ] 339 | }, 340 | { 341 | "name": "Adding attachment", 342 | "request": { 343 | "auth": { 344 | "type": "basic", 345 | "basic": [ 346 | { 347 | "key": "username", 348 | "value": "{{user}}", 349 | "type": "string" 350 | }, 351 | { 352 | "key": "password", 353 | "value": "{{pass}}", 354 | "type": "string" 355 | }, 356 | { 357 | "key": "saveHelperData", 358 | "value": true, 359 | "type": "boolean" 360 | }, 361 | { 362 | "key": "showPassword", 363 | "value": false, 364 | "type": "boolean" 365 | } 366 | ] 367 | }, 368 | "method": "POST", 369 | "header": [ 370 | { 371 | "key": "Content-Type", 372 | "value": "multipart/form-data" 373 | }, 374 | { 375 | "key": "Accept", 376 | "value": "application/json" 377 | } 378 | ], 379 | "body": { 380 | "mode": "formdata", 381 | "formdata": [ 382 | { 383 | "key": "X-Atlassian-Token", 384 | "value": "nocheck", 385 | "type": "text", 386 | "description": "" 387 | }, 388 | { 389 | "key": "file", 390 | "description": "", 391 | "type": "file", 392 | "src": "10311257_1160192464013529_1503935111_n.jpg" 393 | } 394 | ] 395 | }, 396 | "url": { 397 | "raw": "{{url}}/rest/api/2/issue/{{issue}}/attachments", 398 | "host": [ 399 | "{{url}}" 400 | ], 401 | "path": [ 402 | "rest", 403 | "api", 404 | "2", 405 | "issue", 406 | "{{issue}}", 407 | "attachments" 408 | ] 409 | }, 410 | "description": "Creating an issue using the JIRA REST API is as simple as making a POST with a JSON document. To create an issue, you will need to know certain key metadata, like the ID of the project that the issue will be created in, or the ID of the issue type. You can request the create metadata for all issue types across all projects by using the createmeta resource. For example:" 411 | }, 412 | "response": [] 413 | }, 414 | { 415 | "name": "Get meta data", 416 | "request": { 417 | "auth": { 418 | "type": "basic", 419 | "basic": [ 420 | { 421 | "key": "username", 422 | "value": "{{user}}", 423 | "type": "string" 424 | }, 425 | { 426 | "key": "password", 427 | "value": "{{pass}}", 428 | "type": "string" 429 | }, 430 | { 431 | "key": "saveHelperData", 432 | "value": true, 433 | "type": "boolean" 434 | }, 435 | { 436 | "key": "showPassword", 437 | "value": false, 438 | "type": "boolean" 439 | } 440 | ] 441 | }, 442 | "method": "GET", 443 | "header": [ 444 | { 445 | "key": "Content-Type", 446 | "value": "application/json" 447 | }, 448 | { 449 | "key": "Authorization", 450 | "value": "Basic YW5kcmUubGFkZW1hbm46QWhhc2l0YWcxMw==" 451 | } 452 | ], 453 | "body": { 454 | "mode": "raw", 455 | "raw": "{\n \"body\": \"h2. Example comment\\nLi Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles. Ma quande lingues coalesce, li grammatica del resultant lingue es plu simplic e regulari quam ti del coalescent lingues. Li nov lingua franca va esser plu simplic e regulari quam li existent Europan lingues. It va esser tam simplic quam Occidental in fact, it va esser Occidental. A un Angleso it va semblar un simplificat Angles, quam un skeptic Cambridge amico dit me que Occidental es.Li Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles.\"\n}" 456 | }, 457 | "url": { 458 | "raw": "{{url}}/rest/api/2/issue/createmeta", 459 | "host": [ 460 | "{{url}}" 461 | ], 462 | "path": [ 463 | "rest", 464 | "api", 465 | "2", 466 | "issue", 467 | "createmeta" 468 | ] 469 | }, 470 | "description": "" 471 | }, 472 | "response": [] 473 | }, 474 | { 475 | "name": "Search", 476 | "request": { 477 | "auth": { 478 | "type": "basic", 479 | "basic": [ 480 | { 481 | "key": "username", 482 | "value": "{{user}}", 483 | "type": "string" 484 | }, 485 | { 486 | "key": "password", 487 | "value": "{{pass}}", 488 | "type": "string" 489 | }, 490 | { 491 | "key": "saveHelperData", 492 | "value": true, 493 | "type": "boolean" 494 | }, 495 | { 496 | "key": "showPassword", 497 | "value": false, 498 | "type": "boolean" 499 | } 500 | ] 501 | }, 502 | "method": "GET", 503 | "header": [ 504 | { 505 | "key": "Content-Type", 506 | "value": "application/json" 507 | }, 508 | { 509 | "key": "Authorization", 510 | "value": "Basic YW5kcmUubGFkZW1hbm46QWhhc2l0YWcxMw==" 511 | } 512 | ], 513 | "body": { 514 | "mode": "raw", 515 | "raw": "{\n \"body\": \"h2. Example comment\\nLi Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles. Ma quande lingues coalesce, li grammatica del resultant lingue es plu simplic e regulari quam ti del coalescent lingues. Li nov lingua franca va esser plu simplic e regulari quam li existent Europan lingues. It va esser tam simplic quam Occidental in fact, it va esser Occidental. A un Angleso it va semblar un simplificat Angles, quam un skeptic Cambridge amico dit me que Occidental es.Li Europan lingues es membres del sam familie. Lor separat existentie es un myth. Por scientie, musica, sport etc, litot Europa usa li sam vocabular. Li lingues differe solmen in li grammatica, li pronunciation e li plu commun vocabules. Omnicos directe al desirabilite de un nov lingua franca: On refusa continuar payar custosi traductores. At solmen va esser necessi far uniform grammatica, pronunciation e plu sommun paroles.\"\n}" 516 | }, 517 | "url": { 518 | "raw": "{{url}}/rest/api/2/search?jql=assignee={{user}}", 519 | "host": [ 520 | "{{url}}" 521 | ], 522 | "path": [ 523 | "rest", 524 | "api", 525 | "2", 526 | "search" 527 | ], 528 | "query": [ 529 | { 530 | "key": "jql", 531 | "value": "assignee={{user}}", 532 | "equals": true 533 | } 534 | ] 535 | }, 536 | "description": "" 537 | }, 538 | "response": [] 539 | } 540 | ] 541 | } 542 | -------------------------------------------------------------------------------- /spec/resources/jiraDetails.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | url: 'http://jira.com', 3 | user: 'dave', 4 | pass: 'letmein' 5 | } 6 | -------------------------------------------------------------------------------- /spec/resources/sample.out: -------------------------------------------------------------------------------- 1 | Text from file -------------------------------------------------------------------------------- /spec/resources/sample.version: -------------------------------------------------------------------------------- 1 | 1.0.1 -------------------------------------------------------------------------------- /spec/searchBySummary.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const expect = chai.expect; 3 | 4 | const nock = require("nock"); 5 | const out = require("../src/out.js"); 6 | 7 | const jira = require("./resources/jiraDetails.js"); 8 | const concourseInput = require("./resources/concourseInput.js"); 9 | 10 | nock.disableNetConnect(); 11 | 12 | describe("search", () => { 13 | beforeEach(() => { 14 | nock.cleanAll(); 15 | }); 16 | 17 | it("search by summary", done => { 18 | let input = concourseInput(); 19 | 20 | let search = setupSearch({ 21 | jql: 'project="ATP" AND summary~"' + input.params.summary + '" ORDER BY id DESC', 22 | maxResults: 1, 23 | fields: ["key", "summary"] 24 | }); 25 | 26 | out(input, "", () => { 27 | expect(search.isDone()).to.be.true; 28 | done(); 29 | }); 30 | }); 31 | 32 | 33 | it("search by issue_key", done => { 34 | let input = concourseInput(); 35 | input.params.issue_key = "BUILD-1"; 36 | 37 | let search = setupSearch({ 38 | jql: 'project="ATP" AND key="' + input.params.issue_key + '" ORDER BY id DESC', 39 | maxResults: 1, 40 | fields: ["key", "summary"] 41 | }); 42 | 43 | out(input, "", () => { 44 | expect(search.isDone()).to.be.true; 45 | done(); 46 | }); 47 | }); 48 | 49 | 50 | it("search by issue_key and search_filters", done => { 51 | let input = concourseInput(); 52 | input.params.issue_key = "BUILD-1"; 53 | input.params.search_filters = { 54 | status: "Done" 55 | } 56 | 57 | let search = setupSearch({ 58 | jql: 'project="ATP" AND key="' + input.params.issue_key + '" AND status="Done" ORDER BY id DESC', 59 | maxResults: 1, 60 | fields: ["key", "summary"] 61 | }); 62 | 63 | out(input, "", () => { 64 | expect(search.isDone()).to.be.true; 65 | done(); 66 | }); 67 | }); 68 | 69 | 70 | it("search by multiple issue_key and search_filters", done => { 71 | let input = concourseInput(); 72 | input.params.issue_key = "BUILD-1,BUILD-2"; 73 | input.params.search_filters = { 74 | status: "Done" 75 | } 76 | 77 | let search = setupSearch({ 78 | jql: 'project="ATP" AND key IN (' + input.params.issue_key + ') AND status="Done" ORDER BY id DESC', 79 | maxResults: 2, 80 | fields: ["key", "summary"] 81 | }); 82 | 83 | out(input, "", () => { 84 | expect(search.isDone()).to.be.true; 85 | done(); 86 | }); 87 | }); 88 | 89 | it("search by summary and search_filters", done => { 90 | let input = concourseInput(); 91 | input.params.summary = "Test summary"; 92 | input.params.search_filters = { 93 | status: "Done" 94 | } 95 | 96 | let search = setupSearch({ 97 | jql: 'project="ATP" AND summary~"' + input.params.summary + '" AND status="Done" ORDER BY id DESC', 98 | maxResults: 1, 99 | fields: ["key", "summary"] 100 | }); 101 | 102 | out(input, "", () => { 103 | expect(search.isDone()).to.be.true; 104 | done(); 105 | }); 106 | }); 107 | 108 | }); 109 | 110 | 111 | function setupSearch(expectedBody) { 112 | issues = []; 113 | 114 | return nock(jira.url) 115 | .post("/rest/api/2/search/", expectedBody) 116 | .basicAuth({ 117 | user: jira.user, 118 | pass: jira.token 119 | }) 120 | .reply(200, { 121 | expand: "names,schema", 122 | startAt: 0, 123 | maxResults: 1, 124 | total: issues.length, 125 | issues: issues 126 | }); 127 | } 128 | -------------------------------------------------------------------------------- /src/addComments.js: -------------------------------------------------------------------------------- 1 | const async = require('async'); 2 | const debug = require('debug')('jira-resource'); 3 | const request = require('request'); 4 | const replaceTextFileString = require('./utils/replaceTextFileString.js'); 5 | const debugResponse = require('./utils/debugResponse.js'); 6 | 7 | module.exports = (baseFileDir, issue, source, params, callback) => { 8 | if (!issue) { 9 | return callback(null); 10 | } 11 | 12 | if (!params.comments) { 13 | return callback(null, issue); 14 | } 15 | 16 | const commentsUrl = source.url + '/rest/api/2/issue/' + issue.id + '/comment/'; 17 | 18 | debug('Adding comments...'); 19 | 20 | async.each( 21 | params.comments, 22 | (comment, next) => { 23 | const content = replaceTextFileString(baseFileDir, comment.content); 24 | debug('Adding: %s', content); 25 | 26 | request( 27 | { 28 | method: 'POST', 29 | uri: commentsUrl, 30 | auth: { 31 | username: source.email, 32 | password: source.apitoken 33 | }, 34 | json: { 35 | body: content 36 | } 37 | }, 38 | (error, response) => { 39 | debugResponse(response); 40 | next(error); 41 | } 42 | ); 43 | }, 44 | (error) => { 45 | callback(error, issue); 46 | } 47 | ); 48 | }; 49 | -------------------------------------------------------------------------------- /src/addWatchers.js: -------------------------------------------------------------------------------- 1 | const async = require('async'); 2 | const debug = require('debug')('jira-resource'); 3 | const request = require('request'); 4 | 5 | const debugResponse = require('./utils/debugResponse.js'); 6 | 7 | module.exports = (issue, source, params, callback) => { 8 | if (!issue) { 9 | return callback(null); 10 | } 11 | 12 | if (!params.watchers) { 13 | return callback(null, issue); 14 | } 15 | 16 | const watchersUrl = source.url + '/rest/api/2/issue/' + issue.id + '/watchers/'; 17 | 18 | debug('Adding watchers...'); 19 | 20 | async.each( 21 | params.watchers, 22 | (watcher, next) => { 23 | debug('Adding: %s', watcher); 24 | 25 | request( 26 | { 27 | method: 'POST', 28 | uri: watchersUrl, 29 | auth: { 30 | username: source.email, 31 | password: source.apitoken 32 | }, 33 | json: watcher 34 | }, 35 | (error, response) => { 36 | debugResponse(response); 37 | next(error); 38 | } 39 | ); 40 | }, 41 | (error) => { 42 | callback(error, issue); 43 | } 44 | ); 45 | }; 46 | -------------------------------------------------------------------------------- /src/createIssue.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const debug = require('debug')('jira-resource'); 3 | 4 | const requestIssue = require('./utils/requestIssue.js'); 5 | 6 | module.exports = (baseFileDir, source, params, callback) => { 7 | 8 | return createIssue((error, newIssue) => { 9 | if (newIssue) { 10 | callback(error, [newIssue]); 11 | } else { 12 | callback(error); 13 | } 14 | }); 15 | 16 | function createIssue(done) { 17 | debug('Issue doesn\'t exist, creating new issue...'); 18 | 19 | // check before create issue 20 | if (!params.summary) { 21 | return done(new Error('"summary" field is required for creating new issue.')); 22 | } 23 | 24 | return requestIssue(baseFileDir, source, params, source.url + '/rest/api/2/issue/', 'POST', (error, response, body) => { 25 | if (!error && !body) { 26 | return done(new Error('Could not create issue.')); 27 | } 28 | 29 | done(error, body); 30 | }); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /src/out.js: -------------------------------------------------------------------------------- 1 | const async = require('async'); 2 | const debug = require('debug')('jira-resource'); 3 | 4 | const addWatchers = require('./addWatchers.js'); 5 | const createIssue = require('./createIssue.js'); 6 | const updateIssue = require('./updateIssue.js'); 7 | const processTransitions = require('./processTransitions'); 8 | const searchIssues = require('./searchIssues.js'); 9 | const addComments = require('./addComments.js'); 10 | 11 | module.exports = (input, baseFileDir, callback) => { 12 | const source = input.source; 13 | const params = input.params; 14 | 15 | // The Jira API basic auth interface allows the legacy, password-based 16 | // and the new token-based credential combinations all the same, so 17 | // transparently support both types to maintain backwards compatibility. 18 | source.email = source.email || source.username; 19 | source.token = source.token || source.password; 20 | 21 | debug('input params: %s', JSON.stringify(input.params)); 22 | 23 | async.waterfall( 24 | [ 25 | (next) => { 26 | searchIssues(baseFileDir, source, params, next); 27 | }, 28 | (issues, next) => { 29 | if (issues.length == 0) { 30 | createIssue(baseFileDir, source, params, next) 31 | } else { 32 | async.each(issues, function(issue, callback) { 33 | updateIssue(baseFileDir, issue, source, params, callback) 34 | }, function(err) { 35 | if (err) { return next(err) } 36 | else { 37 | next(null, issues); 38 | } 39 | }); 40 | } 41 | }, 42 | (issues, next) => { 43 | async.each(issues, function(issue, callback) { 44 | addWatchers(issue, source, params, callback); 45 | }, function(err) { 46 | if (err) { return next(err) } 47 | else { 48 | next(null, issues); 49 | } 50 | }); 51 | }, 52 | (issues, next) => { 53 | async.each(issues, function(issue, callback) { 54 | processTransitions(issue, source, params, callback); 55 | }, function(err) { 56 | if (err) { return next(err) } 57 | else { 58 | next(null, issues); 59 | } 60 | }); 61 | }, 62 | (issues, next) => { 63 | async.each(issues, function(issue, callback) { 64 | addComments(baseFileDir, issue, source, params, callback); 65 | }, function(err) { 66 | if (err) { return next(err) } 67 | else { 68 | next(null, issues); 69 | } 70 | }); 71 | } 72 | ], 73 | (error, issues) => { 74 | let output = null; 75 | if (issues && issues.length > 0) { 76 | let metadata = []; 77 | for (let issue of issues) { 78 | metadata.push({ 79 | name: issue.key, 80 | value: source.url.replace(/\/$/, "") + '/secure/QuickSearch.jspa?searchString=' + issue.key 81 | }) 82 | } 83 | output = { 84 | version: {ref: 'none'}, 85 | metadata: metadata 86 | }; 87 | } else if (!error) { 88 | error = new Error('Could not create issue.'); 89 | } 90 | 91 | callback(error, output); 92 | } 93 | ); 94 | }; 95 | -------------------------------------------------------------------------------- /src/processTransitions.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const async = require('async'); 3 | const debug = require('debug')('jira-resource'); 4 | const request = require('request'); 5 | 6 | const debugResponse = require('./utils/debugResponse.js'); 7 | 8 | module.exports = (issue, source, params, callback) => { 9 | if (!issue) { 10 | return callback(null); 11 | } 12 | 13 | if (!params.transitions) { 14 | return callback(null, issue); 15 | } 16 | 17 | const transitionUrl = source.url + '/rest/api/2/issue/' + issue.id + '/transitions/'; 18 | 19 | async.eachSeries( 20 | params.transitions, 21 | (nextTransition, next) => { 22 | processTransition(transitionUrl, nextTransition, () => { 23 | next(); 24 | }); 25 | }, 26 | () => { 27 | callback(null, issue); 28 | } 29 | ); 30 | 31 | function processTransition(transitionUrl, transitionName, done) { 32 | async.waterfall( 33 | [ 34 | (next) => { 35 | debug('Searching for available transitions...'); 36 | 37 | request( 38 | { 39 | method: 'GET', 40 | uri: transitionUrl, 41 | auth: { 42 | username: source.email, 43 | password: source.apitoken 44 | }, 45 | json: true 46 | }, 47 | (error, response, body) => { 48 | debugResponse(response); 49 | 50 | let matchTransitions = _.filter(body.transitions, (transition) => { 51 | return transition.name.toLowerCase() == transitionName.toLowerCase(); 52 | }); 53 | if (matchTransitions.length <= 0) { 54 | return done(new Error('Not found for: ' + transitionName + ", choices: " + body.transitions)); 55 | } 56 | let transitionId = matchTransitions[0].id; 57 | 58 | next(error, transitionId); 59 | } 60 | ); 61 | }, 62 | (transitionId, done) => { 63 | debug('Performing transition: %s (%s)', transitionName, transitionId); 64 | 65 | request( 66 | { 67 | method: 'POST', 68 | uri: transitionUrl, 69 | auth: { 70 | username: source.email, 71 | password: source.apitoken 72 | }, 73 | json: { 74 | transition: { 75 | id: transitionId 76 | } 77 | } 78 | }, 79 | (error, response) => { 80 | debugResponse(response); 81 | done(error); 82 | } 83 | ); 84 | } 85 | ], 86 | done 87 | ); 88 | } 89 | }; 90 | -------------------------------------------------------------------------------- /src/searchIssues.js: -------------------------------------------------------------------------------- 1 | const debug = require('debug')('jira-resource'); 2 | const util = require('util'); 3 | const request = require('request'); 4 | 5 | const debugResponse = require('./utils/debugResponse.js'); 6 | const replaceTextFileString = require('./utils/replaceTextFileString.js'); 7 | 8 | 9 | function generateFilterStr(params) { 10 | let custom_filters = []; 11 | if (params.search_filters && Object.keys(params.search_filters).length != 0) { 12 | for (f_key in params.search_filters) { 13 | let f_val = params.search_filters[f_key]; 14 | custom_filters.push(util.format('%s="%s"', f_key, f_val)) 15 | } 16 | } 17 | 18 | let custom_filters_str = ""; 19 | if (custom_filters.length > 0) { 20 | custom_filters_str = "AND " + custom_filters.join(" AND "); 21 | } 22 | 23 | return custom_filters_str 24 | } 25 | 26 | 27 | function searchByIssueKey(baseFileDir, source, params, custom_filter_str) { 28 | const issue_key = replaceTextFileString(baseFileDir, params.issue_key); 29 | debug('Searching for issue by key: %s', issue_key); 30 | 31 | let jql = null; 32 | let maxResults = issue_key.split(",").length; 33 | if (maxResults > 1) { 34 | jql = util.format( 35 | 'project="%s" AND key IN (%s) %s ORDER BY id DESC', 36 | source.project, 37 | issue_key, 38 | custom_filter_str 39 | ) 40 | } else { 41 | jql = util.format( 42 | 'project="%s" AND key="%s" %s ORDER BY id DESC', 43 | source.project, 44 | issue_key, 45 | custom_filter_str 46 | ) 47 | } 48 | return [jql, maxResults] 49 | } 50 | 51 | function searchBySummary(baseFileDir, source, params, custom_filter_str) { 52 | const summary = replaceTextFileString(baseFileDir, params.summary); 53 | debug('Searching for issue by summary: %s', summary); 54 | 55 | let jql = util.format( 56 | 'project="%s" AND summary~"%s" %s ORDER BY id DESC', 57 | source.project, 58 | summary, 59 | custom_filter_str 60 | ) 61 | let maxResults = 1; 62 | return [jql, maxResults] 63 | } 64 | 65 | module.exports = (baseFileDir, source, params, callback) => { 66 | const searchUrl = source.url + '/rest/api/2/search/'; 67 | 68 | let custom_filter_str = generateFilterStr(params); 69 | 70 | let jql = null; 71 | let maxResults = 0; 72 | if (params.issue_key) { 73 | [jql, maxResults] = searchByIssueKey(baseFileDir, source, params, custom_filter_str); 74 | } else { 75 | [jql, maxResults] = searchBySummary(baseFileDir, source, params, custom_filter_str); 76 | } 77 | let search = { 78 | jql: jql, 79 | maxResults: maxResults, 80 | fields: ['key', 'summary'] 81 | }; 82 | 83 | debug('Sending search: %s', jql); 84 | 85 | request( 86 | { 87 | method: 'POST', 88 | uri: searchUrl, 89 | auth: { 90 | username: source.email, 91 | password: source.apitoken 92 | }, 93 | json: search 94 | }, 95 | (error, response, body) => { 96 | debugResponse(response); 97 | 98 | if (error) { 99 | callback(error); 100 | } else if (!error && !body) { 101 | return callback(new Error('Search issue failed.')); 102 | } 103 | 104 | callback(null, body.issues); 105 | } 106 | ); 107 | }; 108 | -------------------------------------------------------------------------------- /src/updateIssue.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const debug = require('debug')('jira-resource'); 3 | const requestIssue = require('./utils/requestIssue.js'); 4 | 5 | module.exports = (baseFileDir, existingIssue, source, params, callback) => { 6 | 7 | return updateIssue((error) => { 8 | callback(error, existingIssue); 9 | }); 10 | 11 | function updateIssue(done) { 12 | let issueId = existingIssue.id; 13 | let issueKey = existingIssue.key; 14 | 15 | debug('Issue exists [%s], updating issue...', issueKey); 16 | 17 | return requestIssue(baseFileDir, source, params, source.url + '/rest/api/2/issue/' + issueId, 'PUT', done); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /src/utils/customFieldFactory.js: -------------------------------------------------------------------------------- 1 | const FreeTextCustomField = require('./freeTextCustomField.js'); 2 | const SelectListCustomField = require('./selectListCustomField.js'); 3 | 4 | module.exports = () => { 5 | function buildCustomField(customField) { 6 | if (!customField.type) { 7 | return new FreeTextCustomField(customField.value); 8 | } 9 | 10 | switch (customField.type.toLowerCase()) { 11 | case 'selectlist': 12 | return new SelectListCustomField(customField.value, customField.value_id); 13 | default: 14 | return new FreeTextCustomField(customField.value); 15 | } 16 | } 17 | 18 | return { 19 | buildCustomField 20 | }; 21 | }; 22 | -------------------------------------------------------------------------------- /src/utils/debugResponse.js: -------------------------------------------------------------------------------- 1 | const debug = require('debug')('jira-resource'); 2 | 3 | module.exports = (response) => { 4 | if (!response) return; 5 | 6 | debug( 7 | 'Response: (%s) %s', 8 | response.statusCode, 9 | response.body ? JSON.stringify(response.body, null, 2) : '-empty body-' 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /src/utils/freeTextCustomField.js: -------------------------------------------------------------------------------- 1 | module.exports = class CustomField { 2 | constructor(value) { 3 | this.value = value; 4 | } 5 | 6 | toApiPayload() { 7 | return this.value; 8 | } 9 | }; 10 | -------------------------------------------------------------------------------- /src/utils/replaceTextFileString.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | module.exports = (baseFileDir, value) => { 4 | 5 | /* 6 | 1. value (string): "some string" 7 | 2. value (object): {text: "some string", file: "file_path"} 8 | 3. value (object): {text: "some string"} 9 | 3. value (object): {file: "file_path"} 10 | */ 11 | if (typeof value !== 'object') { 12 | return value; 13 | } else if (!value.file) { 14 | return value.text; 15 | } 16 | 17 | let filePath = baseFileDir + '/' + value.file; 18 | let fileContent = fs.readFileSync(filePath, 'utf-8').trim(); 19 | return value.text ? value.text.replace('$FILE', fileContent) : fileContent; 20 | }; 21 | -------------------------------------------------------------------------------- /src/utils/requestIssue.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const debug = require('debug')('jira-resource'); 3 | const moment = require('moment'); 4 | const request = require('request'); 5 | 6 | const debugResponse = require('./debugResponse.js'); 7 | const replaceTextFileString = require('./replaceTextFileString.js'); 8 | const customFieldFactory = require('./customFieldFactory.js')(); 9 | 10 | module.exports = (baseFileDir, source, params, issueUrl, method, callback) => { 11 | let issue = { 12 | fields: processFields() 13 | }; 14 | 15 | debug('Sending issue: %s', JSON.stringify(issue, null, 2)); 16 | 17 | request( 18 | { 19 | method: method, 20 | uri: issueUrl, 21 | auth: { 22 | username: source.email, 23 | password: source.apitoken 24 | }, 25 | json: issue 26 | }, 27 | (error, response, body) => { 28 | if (error) { 29 | return callback(error); 30 | } 31 | 32 | debugResponse(response); 33 | 34 | if (response.statusCode < 200 || 300 <= response.statusCode) { 35 | return callback(new Error('Could not update Jira.')); 36 | } 37 | 38 | callback(error, response, body); 39 | } 40 | ); 41 | 42 | function processFields() { 43 | const standardFields = params.fields || {}; 44 | 45 | if (params.summary) { 46 | standardFields.summary = params.summary; 47 | } 48 | 49 | const customFields = parseCustomFields(params); 50 | const nonExpandableCustomFields = _.pickBy(customFields, (value) => { 51 | return typeof value === 'object'; 52 | }); 53 | const expandableCustomFields = _.pickBy(customFields, (value) => { 54 | return typeof value !== 'object'; 55 | }); 56 | const expandableFields = _.merge(expandableCustomFields, standardFields); 57 | 58 | const expandedFields = _(expandableFields) 59 | .mapValues((value) => { 60 | return replaceTextFileString(baseFileDir, value); 61 | }) 62 | .mapValues(replaceNowString) 63 | .value(); 64 | 65 | const fields = _.merge(nonExpandableCustomFields, expandedFields); 66 | 67 | fields.project = { key: source.project }; 68 | 69 | if (params.issue_type) { 70 | fields.issuetype = { name: params.issue_type }; 71 | } 72 | if (params.parent) { 73 | fields.parent = { key: params.parent }; 74 | } 75 | 76 | return fields; 77 | } 78 | 79 | function replaceNowString(value) { 80 | value = String(value); 81 | 82 | return value.replace(/\$NOW([-+][0-9]+)?([ywdhms])?/, (match, change, unit) => { 83 | let date = moment(); 84 | 85 | unit = unit || 'm'; 86 | 87 | if (change) { 88 | date = date.add(change, unit); 89 | } 90 | 91 | return date.format(); 92 | }); 93 | } 94 | 95 | function makeSelectListCustomFieldApiPayload(customField) { 96 | if (value.value_id) { 97 | return { id: value.value_id }; 98 | } 99 | 100 | return { value: value.value }; 101 | } 102 | 103 | function parseCustomFields(params) { 104 | if (!params.custom_fields) { 105 | return {}; 106 | } 107 | 108 | return _(params.custom_fields) 109 | .mapKeys((value) => 'customfield_' + value.id) 110 | .mapValues((value) => customFieldFactory.buildCustomField(value).toApiPayload()) 111 | .value(); 112 | } 113 | }; 114 | -------------------------------------------------------------------------------- /src/utils/selectListCustomField.js: -------------------------------------------------------------------------------- 1 | module.exports = class CustomField { 2 | constructor(value, value_id) { 3 | this.value = value; 4 | this.value_id = value_id; 5 | } 6 | 7 | toApiPayload() { 8 | if (this.value_id) { 9 | return { id: this.value_id.toString() }; 10 | } 11 | 12 | return { value: this.value }; 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | abbrev@1: 6 | version "1.1.1" 7 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" 8 | 9 | abbrev@1.0.x: 10 | version "1.0.9" 11 | resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" 12 | 13 | acorn-jsx@^3.0.0: 14 | version "3.0.1" 15 | resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" 16 | dependencies: 17 | acorn "^3.0.4" 18 | 19 | acorn@^3.0.4: 20 | version "3.3.0" 21 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" 22 | 23 | acorn@^5.5.0: 24 | version "5.6.1" 25 | resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.6.1.tgz#c9e50c3e3717cf897f1b071ceadbb543bbc0a8d4" 26 | 27 | ajv-keywords@^2.1.0: 28 | version "2.1.1" 29 | resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" 30 | 31 | ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: 32 | version "5.5.2" 33 | resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" 34 | dependencies: 35 | co "^4.6.0" 36 | fast-deep-equal "^1.0.0" 37 | fast-json-stable-stringify "^2.0.0" 38 | json-schema-traverse "^0.3.0" 39 | 40 | amdefine@>=0.0.4: 41 | version "1.0.1" 42 | resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" 43 | 44 | ansi-escapes@^3.0.0: 45 | version "3.1.0" 46 | resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" 47 | 48 | ansi-regex@^2.0.0: 49 | version "2.1.1" 50 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" 51 | 52 | ansi-regex@^3.0.0: 53 | version "3.0.0" 54 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" 55 | 56 | ansi-styles@^2.2.1: 57 | version "2.2.1" 58 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" 59 | 60 | ansi-styles@^3.1.0, ansi-styles@^3.2.0, ansi-styles@^3.2.1: 61 | version "3.2.1" 62 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" 63 | dependencies: 64 | color-convert "^1.9.0" 65 | 66 | argparse@^1.0.7: 67 | version "1.0.10" 68 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" 69 | dependencies: 70 | sprintf-js "~1.0.2" 71 | 72 | array-union@^1.0.1: 73 | version "1.0.2" 74 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" 75 | dependencies: 76 | array-uniq "^1.0.1" 77 | 78 | array-uniq@^1.0.1: 79 | version "1.0.3" 80 | resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" 81 | 82 | arrify@^1.0.0, arrify@^1.0.1: 83 | version "1.0.1" 84 | resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" 85 | 86 | asn1@~0.2.3: 87 | version "0.2.3" 88 | resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" 89 | 90 | assert-plus@1.0.0, assert-plus@^1.0.0: 91 | version "1.0.0" 92 | resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" 93 | 94 | assertion-error@^1.0.1: 95 | version "1.1.0" 96 | resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" 97 | 98 | async@1.x: 99 | version "1.5.2" 100 | resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" 101 | 102 | async@^2.0.0-rc.6: 103 | version "2.6.1" 104 | resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" 105 | dependencies: 106 | lodash "^4.17.10" 107 | 108 | asynckit@^0.4.0: 109 | version "0.4.0" 110 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 111 | 112 | aws-sign2@~0.7.0: 113 | version "0.7.0" 114 | resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" 115 | 116 | aws4@^1.6.0: 117 | version "1.7.0" 118 | resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" 119 | 120 | babel-code-frame@^6.22.0: 121 | version "6.26.0" 122 | resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" 123 | dependencies: 124 | chalk "^1.1.3" 125 | esutils "^2.0.2" 126 | js-tokens "^3.0.2" 127 | 128 | babel-runtime@^6.23.0, babel-runtime@^6.26.0: 129 | version "6.26.0" 130 | resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" 131 | dependencies: 132 | core-js "^2.4.0" 133 | regenerator-runtime "^0.11.0" 134 | 135 | balanced-match@^1.0.0: 136 | version "1.0.0" 137 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" 138 | 139 | bcrypt-pbkdf@^1.0.0: 140 | version "1.0.1" 141 | resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" 142 | dependencies: 143 | tweetnacl "^0.14.3" 144 | 145 | boolify@^1.0.0: 146 | version "1.0.1" 147 | resolved "https://registry.yarnpkg.com/boolify/-/boolify-1.0.1.tgz#b5c09e17cacd113d11b7bb3ed384cc012994d86b" 148 | 149 | brace-expansion@^1.1.7: 150 | version "1.1.11" 151 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" 152 | dependencies: 153 | balanced-match "^1.0.0" 154 | concat-map "0.0.1" 155 | 156 | browser-stdout@1.3.1: 157 | version "1.3.1" 158 | resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" 159 | 160 | buffer-from@^1.0.0: 161 | version "1.1.0" 162 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.0.tgz#87fcaa3a298358e0ade6e442cfce840740d1ad04" 163 | 164 | caller-path@^0.1.0: 165 | version "0.1.0" 166 | resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" 167 | dependencies: 168 | callsites "^0.2.0" 169 | 170 | callsites@^0.2.0: 171 | version "0.2.0" 172 | resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" 173 | 174 | camelcase-keys@^4.1.0: 175 | version "4.2.0" 176 | resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" 177 | dependencies: 178 | camelcase "^4.1.0" 179 | map-obj "^2.0.0" 180 | quick-lru "^1.0.0" 181 | 182 | camelcase@^4.1.0: 183 | version "4.1.0" 184 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" 185 | 186 | caseless@~0.12.0: 187 | version "0.12.0" 188 | resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" 189 | 190 | chai@^3.5.0: 191 | version "3.5.0" 192 | resolved "https://registry.yarnpkg.com/chai/-/chai-3.5.0.tgz#4d02637b067fe958bdbfdd3a40ec56fef7373247" 193 | dependencies: 194 | assertion-error "^1.0.1" 195 | deep-eql "^0.1.3" 196 | type-detect "^1.0.0" 197 | 198 | chai@^4.1.2: 199 | version "4.1.2" 200 | resolved "https://registry.yarnpkg.com/chai/-/chai-4.1.2.tgz#0f64584ba642f0f2ace2806279f4f06ca23ad73c" 201 | dependencies: 202 | assertion-error "^1.0.1" 203 | check-error "^1.0.1" 204 | deep-eql "^3.0.0" 205 | get-func-name "^2.0.0" 206 | pathval "^1.0.0" 207 | type-detect "^4.0.0" 208 | 209 | chalk@2.3.0: 210 | version "2.3.0" 211 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.0.tgz#b5ea48efc9c1793dccc9b4767c93914d3f2d52ba" 212 | dependencies: 213 | ansi-styles "^3.1.0" 214 | escape-string-regexp "^1.0.5" 215 | supports-color "^4.0.0" 216 | 217 | chalk@^1.1.3: 218 | version "1.1.3" 219 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" 220 | dependencies: 221 | ansi-styles "^2.2.1" 222 | escape-string-regexp "^1.0.2" 223 | has-ansi "^2.0.0" 224 | strip-ansi "^3.0.0" 225 | supports-color "^2.0.0" 226 | 227 | chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.0: 228 | version "2.4.1" 229 | resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" 230 | dependencies: 231 | ansi-styles "^3.2.1" 232 | escape-string-regexp "^1.0.5" 233 | supports-color "^5.3.0" 234 | 235 | chardet@^0.4.0: 236 | version "0.4.2" 237 | resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" 238 | 239 | charenc@~0.0.1: 240 | version "0.0.2" 241 | resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" 242 | 243 | check-error@^1.0.1: 244 | version "1.0.2" 245 | resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" 246 | 247 | ci-info@^1.0.0: 248 | version "1.1.3" 249 | resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.1.3.tgz#710193264bb05c77b8c90d02f5aaf22216a667b2" 250 | 251 | circular-json@^0.3.1: 252 | version "0.3.3" 253 | resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" 254 | 255 | cli-cursor@^2.1.0: 256 | version "2.1.0" 257 | resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" 258 | dependencies: 259 | restore-cursor "^2.0.0" 260 | 261 | cli-width@^2.0.0: 262 | version "2.2.0" 263 | resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" 264 | 265 | cliui@^3.2.0: 266 | version "3.2.0" 267 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" 268 | dependencies: 269 | string-width "^1.0.1" 270 | strip-ansi "^3.0.1" 271 | wrap-ansi "^2.0.0" 272 | 273 | co@^4.6.0: 274 | version "4.6.0" 275 | resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" 276 | 277 | code-point-at@^1.0.0: 278 | version "1.1.0" 279 | resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" 280 | 281 | color-convert@^1.9.0: 282 | version "1.9.1" 283 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" 284 | dependencies: 285 | color-name "^1.1.1" 286 | 287 | color-name@^1.1.1: 288 | version "1.1.3" 289 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" 290 | 291 | combined-stream@1.0.6, combined-stream@~1.0.5: 292 | version "1.0.6" 293 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" 294 | dependencies: 295 | delayed-stream "~1.0.0" 296 | 297 | commander@2.15.1: 298 | version "2.15.1" 299 | resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" 300 | 301 | common-tags@^1.4.0: 302 | version "1.8.0" 303 | resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" 304 | 305 | concat-map@0.0.1: 306 | version "0.0.1" 307 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" 308 | 309 | concat-stream@^1.6.0: 310 | version "1.6.2" 311 | resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" 312 | dependencies: 313 | buffer-from "^1.0.0" 314 | inherits "^2.0.3" 315 | readable-stream "^2.2.2" 316 | typedarray "^0.0.6" 317 | 318 | core-js@^2.4.0: 319 | version "2.5.7" 320 | resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" 321 | 322 | core-util-is@1.0.2, core-util-is@~1.0.0: 323 | version "1.0.2" 324 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 325 | 326 | cross-spawn@^5.0.1, cross-spawn@^5.1.0: 327 | version "5.1.0" 328 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" 329 | dependencies: 330 | lru-cache "^4.0.1" 331 | shebang-command "^1.2.0" 332 | which "^1.2.9" 333 | 334 | crypt@~0.0.1: 335 | version "0.0.2" 336 | resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" 337 | 338 | dashdash@^1.12.0: 339 | version "1.14.1" 340 | resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" 341 | dependencies: 342 | assert-plus "^1.0.0" 343 | 344 | debug@3.1.0, debug@^3.1.0: 345 | version "3.1.0" 346 | resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" 347 | dependencies: 348 | ms "2.0.0" 349 | 350 | debug@^2.2.0: 351 | version "2.6.9" 352 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 353 | dependencies: 354 | ms "2.0.0" 355 | 356 | decamelize@^1.1.1: 357 | version "1.2.0" 358 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 359 | 360 | deep-eql@^0.1.3: 361 | version "0.1.3" 362 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-0.1.3.tgz#ef558acab8de25206cd713906d74e56930eb69f2" 363 | dependencies: 364 | type-detect "0.1.1" 365 | 366 | deep-eql@^3.0.0: 367 | version "3.0.1" 368 | resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" 369 | dependencies: 370 | type-detect "^4.0.0" 371 | 372 | deep-equal@^1.0.0: 373 | version "1.0.1" 374 | resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" 375 | 376 | deep-is@~0.1.3: 377 | version "0.1.3" 378 | resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" 379 | 380 | del@^2.0.2: 381 | version "2.2.2" 382 | resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" 383 | dependencies: 384 | globby "^5.0.0" 385 | is-path-cwd "^1.0.0" 386 | is-path-in-cwd "^1.0.0" 387 | object-assign "^4.0.1" 388 | pify "^2.0.0" 389 | pinkie-promise "^2.0.0" 390 | rimraf "^2.2.8" 391 | 392 | delayed-stream@~1.0.0: 393 | version "1.0.0" 394 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 395 | 396 | diff@3.5.0: 397 | version "3.5.0" 398 | resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" 399 | 400 | dlv@^1.1.0: 401 | version "1.1.2" 402 | resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.2.tgz#270f6737b30d25b6657a7e962c784403f85137e5" 403 | 404 | doctrine@^2.1.0: 405 | version "2.1.0" 406 | resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" 407 | dependencies: 408 | esutils "^2.0.2" 409 | 410 | ecc-jsbn@~0.1.1: 411 | version "0.1.1" 412 | resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" 413 | dependencies: 414 | jsbn "~0.1.0" 415 | 416 | escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: 417 | version "1.0.5" 418 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" 419 | 420 | escodegen@1.8.x: 421 | version "1.8.1" 422 | resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" 423 | dependencies: 424 | esprima "^2.7.1" 425 | estraverse "^1.9.1" 426 | esutils "^2.0.2" 427 | optionator "^0.8.1" 428 | optionalDependencies: 429 | source-map "~0.2.0" 430 | 431 | eslint-scope@^3.7.1: 432 | version "3.7.1" 433 | resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" 434 | dependencies: 435 | esrecurse "^4.1.0" 436 | estraverse "^4.1.1" 437 | 438 | eslint-visitor-keys@^1.0.0: 439 | version "1.0.0" 440 | resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" 441 | 442 | eslint@^4.0.0, eslint@^4.19.1, eslint@^4.5.0: 443 | version "4.19.1" 444 | resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" 445 | dependencies: 446 | ajv "^5.3.0" 447 | babel-code-frame "^6.22.0" 448 | chalk "^2.1.0" 449 | concat-stream "^1.6.0" 450 | cross-spawn "^5.1.0" 451 | debug "^3.1.0" 452 | doctrine "^2.1.0" 453 | eslint-scope "^3.7.1" 454 | eslint-visitor-keys "^1.0.0" 455 | espree "^3.5.4" 456 | esquery "^1.0.0" 457 | esutils "^2.0.2" 458 | file-entry-cache "^2.0.0" 459 | functional-red-black-tree "^1.0.1" 460 | glob "^7.1.2" 461 | globals "^11.0.1" 462 | ignore "^3.3.3" 463 | imurmurhash "^0.1.4" 464 | inquirer "^3.0.6" 465 | is-resolvable "^1.0.0" 466 | js-yaml "^3.9.1" 467 | json-stable-stringify-without-jsonify "^1.0.1" 468 | levn "^0.3.0" 469 | lodash "^4.17.4" 470 | minimatch "^3.0.2" 471 | mkdirp "^0.5.1" 472 | natural-compare "^1.4.0" 473 | optionator "^0.8.2" 474 | path-is-inside "^1.0.2" 475 | pluralize "^7.0.0" 476 | progress "^2.0.0" 477 | regexpp "^1.0.1" 478 | require-uncached "^1.0.3" 479 | semver "^5.3.0" 480 | strip-ansi "^4.0.0" 481 | strip-json-comments "~2.0.1" 482 | table "4.0.2" 483 | text-table "~0.2.0" 484 | 485 | espree@^3.5.2, espree@^3.5.4: 486 | version "3.5.4" 487 | resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" 488 | dependencies: 489 | acorn "^5.5.0" 490 | acorn-jsx "^3.0.0" 491 | 492 | esprima@2.7.x, esprima@^2.7.1: 493 | version "2.7.3" 494 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" 495 | 496 | esprima@^4.0.0: 497 | version "4.0.1" 498 | resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" 499 | 500 | esquery@^1.0.0: 501 | version "1.0.1" 502 | resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" 503 | dependencies: 504 | estraverse "^4.0.0" 505 | 506 | esrecurse@^4.1.0: 507 | version "4.2.1" 508 | resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" 509 | dependencies: 510 | estraverse "^4.1.0" 511 | 512 | estraverse@^1.9.1: 513 | version "1.9.3" 514 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" 515 | 516 | estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: 517 | version "4.2.0" 518 | resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" 519 | 520 | esutils@^2.0.2: 521 | version "2.0.2" 522 | resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" 523 | 524 | execa@^0.7.0: 525 | version "0.7.0" 526 | resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" 527 | dependencies: 528 | cross-spawn "^5.0.1" 529 | get-stream "^3.0.0" 530 | is-stream "^1.1.0" 531 | npm-run-path "^2.0.0" 532 | p-finally "^1.0.0" 533 | signal-exit "^3.0.0" 534 | strip-eof "^1.0.0" 535 | 536 | execa@^0.8.0: 537 | version "0.8.0" 538 | resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" 539 | dependencies: 540 | cross-spawn "^5.0.1" 541 | get-stream "^3.0.0" 542 | is-stream "^1.1.0" 543 | npm-run-path "^2.0.0" 544 | p-finally "^1.0.0" 545 | signal-exit "^3.0.0" 546 | strip-eof "^1.0.0" 547 | 548 | extend@~3.0.1: 549 | version "3.0.2" 550 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" 551 | 552 | external-editor@^2.0.4: 553 | version "2.2.0" 554 | resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" 555 | dependencies: 556 | chardet "^0.4.0" 557 | iconv-lite "^0.4.17" 558 | tmp "^0.0.33" 559 | 560 | extsprintf@1.3.0: 561 | version "1.3.0" 562 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" 563 | 564 | extsprintf@^1.2.0: 565 | version "1.4.0" 566 | resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" 567 | 568 | fast-deep-equal@^1.0.0: 569 | version "1.1.0" 570 | resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" 571 | 572 | fast-json-stable-stringify@^2.0.0: 573 | version "2.0.0" 574 | resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" 575 | 576 | fast-levenshtein@~2.0.4: 577 | version "2.0.6" 578 | resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" 579 | 580 | figures@^2.0.0: 581 | version "2.0.0" 582 | resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" 583 | dependencies: 584 | escape-string-regexp "^1.0.5" 585 | 586 | file-entry-cache@^2.0.0: 587 | version "2.0.0" 588 | resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" 589 | dependencies: 590 | flat-cache "^1.2.1" 591 | object-assign "^4.0.1" 592 | 593 | find-up@^2.1.0: 594 | version "2.1.0" 595 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" 596 | dependencies: 597 | locate-path "^2.0.0" 598 | 599 | flat-cache@^1.2.1: 600 | version "1.3.0" 601 | resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" 602 | dependencies: 603 | circular-json "^0.3.1" 604 | del "^2.0.2" 605 | graceful-fs "^4.1.2" 606 | write "^0.2.1" 607 | 608 | forever-agent@~0.6.1: 609 | version "0.6.1" 610 | resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" 611 | 612 | form-data@~2.3.1: 613 | version "2.3.2" 614 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" 615 | dependencies: 616 | asynckit "^0.4.0" 617 | combined-stream "1.0.6" 618 | mime-types "^2.1.12" 619 | 620 | fs.realpath@^1.0.0: 621 | version "1.0.0" 622 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" 623 | 624 | functional-red-black-tree@^1.0.1: 625 | version "1.0.1" 626 | resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" 627 | 628 | get-caller-file@^1.0.1: 629 | version "1.0.3" 630 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" 631 | 632 | get-func-name@^2.0.0: 633 | version "2.0.0" 634 | resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" 635 | 636 | get-stdin@^5.0.1: 637 | version "5.0.1" 638 | resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-5.0.1.tgz#122e161591e21ff4c52530305693f20e6393a398" 639 | 640 | get-stream@^3.0.0: 641 | version "3.0.0" 642 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" 643 | 644 | getpass@^0.1.1: 645 | version "0.1.7" 646 | resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" 647 | dependencies: 648 | assert-plus "^1.0.0" 649 | 650 | glob@7.1.2, glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: 651 | version "7.1.2" 652 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" 653 | dependencies: 654 | fs.realpath "^1.0.0" 655 | inflight "^1.0.4" 656 | inherits "2" 657 | minimatch "^3.0.4" 658 | once "^1.3.0" 659 | path-is-absolute "^1.0.0" 660 | 661 | glob@^5.0.15: 662 | version "5.0.15" 663 | resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" 664 | dependencies: 665 | inflight "^1.0.4" 666 | inherits "2" 667 | minimatch "2 || 3" 668 | once "^1.3.0" 669 | path-is-absolute "^1.0.0" 670 | 671 | glob@^7.1.1: 672 | version "7.1.3" 673 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" 674 | dependencies: 675 | fs.realpath "^1.0.0" 676 | inflight "^1.0.4" 677 | inherits "2" 678 | minimatch "^3.0.4" 679 | once "^1.3.0" 680 | path-is-absolute "^1.0.0" 681 | 682 | glob@~7.0.6: 683 | version "7.0.6" 684 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.0.6.tgz#211bafaf49e525b8cd93260d14ab136152b3f57a" 685 | dependencies: 686 | fs.realpath "^1.0.0" 687 | inflight "^1.0.4" 688 | inherits "2" 689 | minimatch "^3.0.2" 690 | once "^1.3.0" 691 | path-is-absolute "^1.0.0" 692 | 693 | globals@^11.0.1: 694 | version "11.5.0" 695 | resolved "https://registry.yarnpkg.com/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642" 696 | 697 | globby@^5.0.0: 698 | version "5.0.0" 699 | resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" 700 | dependencies: 701 | array-union "^1.0.1" 702 | arrify "^1.0.0" 703 | glob "^7.0.3" 704 | object-assign "^4.0.1" 705 | pify "^2.0.0" 706 | pinkie-promise "^2.0.0" 707 | 708 | graceful-fs@^4.1.2: 709 | version "4.1.11" 710 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" 711 | 712 | growl@1.10.5: 713 | version "1.10.5" 714 | resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" 715 | 716 | handlebars@^4.0.1: 717 | version "4.7.6" 718 | resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.6.tgz#d4c05c1baf90e9945f77aa68a7a219aa4a7df74e" 719 | dependencies: 720 | minimist "^1.2.5" 721 | neo-async "^2.6.0" 722 | source-map "^0.6.1" 723 | wordwrap "^1.0.0" 724 | optionalDependencies: 725 | uglify-js "^3.1.4" 726 | 727 | har-schema@^2.0.0: 728 | version "2.0.0" 729 | resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" 730 | 731 | har-validator@~5.0.3: 732 | version "5.0.3" 733 | resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" 734 | dependencies: 735 | ajv "^5.1.0" 736 | har-schema "^2.0.0" 737 | 738 | has-ansi@^2.0.0: 739 | version "2.0.0" 740 | resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" 741 | dependencies: 742 | ansi-regex "^2.0.0" 743 | 744 | has-flag@^1.0.0: 745 | version "1.0.0" 746 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" 747 | 748 | has-flag@^2.0.0: 749 | version "2.0.0" 750 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" 751 | 752 | has-flag@^3.0.0: 753 | version "3.0.0" 754 | resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" 755 | 756 | he@1.1.1: 757 | version "1.1.1" 758 | resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" 759 | 760 | http-signature@~1.2.0: 761 | version "1.2.0" 762 | resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" 763 | dependencies: 764 | assert-plus "^1.0.0" 765 | jsprim "^1.2.2" 766 | sshpk "^1.7.0" 767 | 768 | husky@^0.14.3: 769 | version "0.14.3" 770 | resolved "https://registry.yarnpkg.com/husky/-/husky-0.14.3.tgz#c69ed74e2d2779769a17ba8399b54ce0b63c12c3" 771 | dependencies: 772 | is-ci "^1.0.10" 773 | normalize-path "^1.0.0" 774 | strip-indent "^2.0.0" 775 | 776 | iconv-lite@^0.4.17: 777 | version "0.4.23" 778 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" 779 | dependencies: 780 | safer-buffer ">= 2.1.2 < 3" 781 | 782 | ignore@^3.2.7: 783 | version "3.3.10" 784 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" 785 | 786 | ignore@^3.3.3, ignore@^3.3.7: 787 | version "3.3.8" 788 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.8.tgz#3f8e9c35d38708a3a7e0e9abb6c73e7ee7707b2b" 789 | 790 | imurmurhash@^0.1.4: 791 | version "0.1.4" 792 | resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" 793 | 794 | indent-string@^3.1.0, indent-string@^3.2.0: 795 | version "3.2.0" 796 | resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" 797 | 798 | inflight@^1.0.4: 799 | version "1.0.6" 800 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" 801 | dependencies: 802 | once "^1.3.0" 803 | wrappy "1" 804 | 805 | inherits@2, inherits@^2.0.3, inherits@~2.0.3: 806 | version "2.0.3" 807 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 808 | 809 | inquirer@^3.0.6: 810 | version "3.3.0" 811 | resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" 812 | dependencies: 813 | ansi-escapes "^3.0.0" 814 | chalk "^2.0.0" 815 | cli-cursor "^2.1.0" 816 | cli-width "^2.0.0" 817 | external-editor "^2.0.4" 818 | figures "^2.0.0" 819 | lodash "^4.3.0" 820 | mute-stream "0.0.7" 821 | run-async "^2.2.0" 822 | rx-lite "^4.0.8" 823 | rx-lite-aggregates "^4.0.8" 824 | string-width "^2.1.0" 825 | strip-ansi "^4.0.0" 826 | through "^2.3.6" 827 | 828 | invert-kv@^1.0.0: 829 | version "1.0.0" 830 | resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" 831 | 832 | irregular-plurals@^1.0.0: 833 | version "1.4.0" 834 | resolved "https://registry.yarnpkg.com/irregular-plurals/-/irregular-plurals-1.4.0.tgz#2ca9b033651111855412f16be5d77c62a458a766" 835 | 836 | is-buffer@~1.1.1: 837 | version "1.1.6" 838 | resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" 839 | 840 | is-ci@^1.0.10: 841 | version "1.1.0" 842 | resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-1.1.0.tgz#247e4162e7860cebbdaf30b774d6b0ac7dcfe7a5" 843 | dependencies: 844 | ci-info "^1.0.0" 845 | 846 | is-fullwidth-code-point@^1.0.0: 847 | version "1.0.0" 848 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" 849 | dependencies: 850 | number-is-nan "^1.0.0" 851 | 852 | is-fullwidth-code-point@^2.0.0: 853 | version "2.0.0" 854 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" 855 | 856 | is-path-cwd@^1.0.0: 857 | version "1.0.0" 858 | resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" 859 | 860 | is-path-in-cwd@^1.0.0: 861 | version "1.0.1" 862 | resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" 863 | dependencies: 864 | is-path-inside "^1.0.0" 865 | 866 | is-path-inside@^1.0.0: 867 | version "1.0.1" 868 | resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" 869 | dependencies: 870 | path-is-inside "^1.0.1" 871 | 872 | is-promise@^2.1.0: 873 | version "2.1.0" 874 | resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" 875 | 876 | is-resolvable@^1.0.0: 877 | version "1.1.0" 878 | resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" 879 | 880 | is-stream@^1.1.0: 881 | version "1.1.0" 882 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" 883 | 884 | is-typedarray@~1.0.0: 885 | version "1.0.0" 886 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" 887 | 888 | isarray@~1.0.0: 889 | version "1.0.0" 890 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 891 | 892 | isexe@^2.0.0: 893 | version "2.0.0" 894 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" 895 | 896 | isstream@~0.1.2: 897 | version "0.1.2" 898 | resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" 899 | 900 | istanbul@^0.4.5: 901 | version "0.4.5" 902 | resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" 903 | dependencies: 904 | abbrev "1.0.x" 905 | async "1.x" 906 | escodegen "1.8.x" 907 | esprima "2.7.x" 908 | glob "^5.0.15" 909 | handlebars "^4.0.1" 910 | js-yaml "3.x" 911 | mkdirp "0.5.x" 912 | nopt "3.x" 913 | once "1.x" 914 | resolve "1.1.x" 915 | supports-color "^3.1.0" 916 | which "^1.1.1" 917 | wordwrap "^1.0.0" 918 | 919 | js-tokens@^3.0.2: 920 | version "3.0.2" 921 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" 922 | 923 | js-yaml@3.x, js-yaml@^3.9.1: 924 | version "3.13.1" 925 | resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" 926 | dependencies: 927 | argparse "^1.0.7" 928 | esprima "^4.0.0" 929 | 930 | jsbn@~0.1.0: 931 | version "0.1.1" 932 | resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" 933 | 934 | json-schema-traverse@^0.3.0: 935 | version "0.3.1" 936 | resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" 937 | 938 | json-schema@0.2.3: 939 | version "0.2.3" 940 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" 941 | 942 | json-stable-stringify-without-jsonify@^1.0.1: 943 | version "1.0.1" 944 | resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" 945 | 946 | json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: 947 | version "5.0.1" 948 | resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" 949 | 950 | jsprim@^1.2.2: 951 | version "1.4.1" 952 | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" 953 | dependencies: 954 | assert-plus "1.0.0" 955 | extsprintf "1.3.0" 956 | json-schema "0.2.3" 957 | verror "1.10.0" 958 | 959 | lcid@^1.0.0: 960 | version "1.0.0" 961 | resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" 962 | dependencies: 963 | invert-kv "^1.0.0" 964 | 965 | levn@^0.3.0, levn@~0.3.0: 966 | version "0.3.0" 967 | resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" 968 | dependencies: 969 | prelude-ls "~1.1.2" 970 | type-check "~0.3.2" 971 | 972 | locate-path@^2.0.0: 973 | version "2.0.0" 974 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" 975 | dependencies: 976 | p-locate "^2.0.0" 977 | path-exists "^3.0.0" 978 | 979 | lodash.memoize@^4.1.2: 980 | version "4.1.2" 981 | resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" 982 | 983 | lodash.merge@^4.6.0: 984 | version "4.6.2" 985 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" 986 | 987 | lodash.unescape@4.0.1: 988 | version "4.0.1" 989 | resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" 990 | 991 | lodash@^4.17.10, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0: 992 | version "4.17.13" 993 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93" 994 | 995 | lodash@^4.17.16: 996 | version "4.17.20" 997 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" 998 | 999 | loglevel-colored-level-prefix@^1.0.0: 1000 | version "1.0.0" 1001 | resolved "https://registry.yarnpkg.com/loglevel-colored-level-prefix/-/loglevel-colored-level-prefix-1.0.0.tgz#6a40218fdc7ae15fc76c3d0f3e676c465388603e" 1002 | dependencies: 1003 | chalk "^1.1.3" 1004 | loglevel "^1.4.1" 1005 | 1006 | loglevel@^1.4.1: 1007 | version "1.6.1" 1008 | resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" 1009 | 1010 | lru-cache@^4.0.1: 1011 | version "4.1.3" 1012 | resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" 1013 | dependencies: 1014 | pseudomap "^1.0.2" 1015 | yallist "^2.1.2" 1016 | 1017 | make-plural@^4.1.1: 1018 | version "4.2.0" 1019 | resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-4.2.0.tgz#03edfc34a2aee630a57e209369ef26ee3ca69590" 1020 | optionalDependencies: 1021 | minimist "^1.2.0" 1022 | 1023 | map-obj@^2.0.0: 1024 | version "2.0.0" 1025 | resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" 1026 | 1027 | md5@^2.1.0: 1028 | version "2.2.1" 1029 | resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" 1030 | dependencies: 1031 | charenc "~0.0.1" 1032 | crypt "~0.0.1" 1033 | is-buffer "~1.1.1" 1034 | 1035 | mem@^1.1.0: 1036 | version "1.1.0" 1037 | resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" 1038 | dependencies: 1039 | mimic-fn "^1.0.0" 1040 | 1041 | messageformat-parser@^1.1.0: 1042 | version "1.1.0" 1043 | resolved "https://registry.yarnpkg.com/messageformat-parser/-/messageformat-parser-1.1.0.tgz#13ba2250a76bbde8e0fca0dbb3475f95c594a90a" 1044 | 1045 | messageformat@^1.0.2: 1046 | version "1.1.1" 1047 | resolved "https://registry.yarnpkg.com/messageformat/-/messageformat-1.1.1.tgz#ceaa2e6c86929d4807058275a7372b1bd963bdf6" 1048 | dependencies: 1049 | glob "~7.0.6" 1050 | make-plural "^4.1.1" 1051 | messageformat-parser "^1.1.0" 1052 | nopt "~3.0.6" 1053 | reserved-words "^0.1.2" 1054 | 1055 | mime-db@~1.33.0: 1056 | version "1.33.0" 1057 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" 1058 | 1059 | mime-types@^2.1.12, mime-types@~2.1.17: 1060 | version "2.1.18" 1061 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" 1062 | dependencies: 1063 | mime-db "~1.33.0" 1064 | 1065 | mimic-fn@^1.0.0: 1066 | version "1.2.0" 1067 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" 1068 | 1069 | "minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: 1070 | version "3.0.4" 1071 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" 1072 | dependencies: 1073 | brace-expansion "^1.1.7" 1074 | 1075 | minimist@0.0.8: 1076 | version "0.0.8" 1077 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" 1078 | 1079 | minimist@^1.2.0, minimist@^1.2.5: 1080 | version "1.2.5" 1081 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" 1082 | 1083 | mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.1: 1084 | version "0.5.1" 1085 | resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" 1086 | dependencies: 1087 | minimist "0.0.8" 1088 | 1089 | mocha-junit-reporter@^1.17.0: 1090 | version "1.17.0" 1091 | resolved "https://registry.yarnpkg.com/mocha-junit-reporter/-/mocha-junit-reporter-1.17.0.tgz#2e5149ed40fc5d2e3ca71e42db5ab1fec9c6d85c" 1092 | dependencies: 1093 | debug "^2.2.0" 1094 | md5 "^2.1.0" 1095 | mkdirp "~0.5.1" 1096 | strip-ansi "^4.0.0" 1097 | xml "^1.0.0" 1098 | 1099 | mocha-lcov-reporter@^1.3.0: 1100 | version "1.3.0" 1101 | resolved "https://registry.yarnpkg.com/mocha-lcov-reporter/-/mocha-lcov-reporter-1.3.0.tgz#469bdef4f8afc9a116056f079df6182d0afb0384" 1102 | 1103 | mocha-simple-html-reporter@^1.1.0: 1104 | version "1.1.0" 1105 | resolved "https://registry.yarnpkg.com/mocha-simple-html-reporter/-/mocha-simple-html-reporter-1.1.0.tgz#4baf1fbadb80e6554ac53709f0416f3c80024e7d" 1106 | dependencies: 1107 | pretty-ms "3.0.1" 1108 | 1109 | mocha@^5.2.0: 1110 | version "5.2.0" 1111 | resolved "https://registry.yarnpkg.com/mocha/-/mocha-5.2.0.tgz#6d8ae508f59167f940f2b5b3c4a612ae50c90ae6" 1112 | dependencies: 1113 | browser-stdout "1.3.1" 1114 | commander "2.15.1" 1115 | debug "3.1.0" 1116 | diff "3.5.0" 1117 | escape-string-regexp "1.0.5" 1118 | glob "7.1.2" 1119 | growl "1.10.5" 1120 | he "1.1.1" 1121 | minimatch "3.0.4" 1122 | mkdirp "0.5.1" 1123 | supports-color "5.4.0" 1124 | 1125 | moment@^2.13.0: 1126 | version "2.22.2" 1127 | resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.2.tgz#3c257f9839fc0e93ff53149632239eb90783ff66" 1128 | 1129 | mri@^1.1.0: 1130 | version "1.1.1" 1131 | resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.1.tgz#85aa26d3daeeeedf80dc5984af95cc5ca5cad9f1" 1132 | 1133 | ms@2.0.0: 1134 | version "2.0.0" 1135 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 1136 | 1137 | mute-stream@0.0.7: 1138 | version "0.0.7" 1139 | resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" 1140 | 1141 | natural-compare@^1.4.0: 1142 | version "1.4.0" 1143 | resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" 1144 | 1145 | neo-async@^2.6.0: 1146 | version "2.6.2" 1147 | resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" 1148 | 1149 | nock@^9.2.6: 1150 | version "9.3.0" 1151 | resolved "https://registry.yarnpkg.com/nock/-/nock-9.3.0.tgz#4dcfdd37bd249836754d05bbac5a1f05e12e0f16" 1152 | dependencies: 1153 | chai "^4.1.2" 1154 | debug "^3.1.0" 1155 | deep-equal "^1.0.0" 1156 | json-stringify-safe "^5.0.1" 1157 | lodash "^4.17.5" 1158 | mkdirp "^0.5.0" 1159 | propagate "^1.0.0" 1160 | qs "^6.5.1" 1161 | semver "^5.5.0" 1162 | 1163 | nopt@3.x, nopt@~3.0.6: 1164 | version "3.0.6" 1165 | resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" 1166 | dependencies: 1167 | abbrev "1" 1168 | 1169 | normalize-path@^1.0.0: 1170 | version "1.0.0" 1171 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-1.0.0.tgz#32d0e472f91ff345701c15a8311018d3b0a90379" 1172 | 1173 | npm-run-path@^2.0.0: 1174 | version "2.0.2" 1175 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" 1176 | dependencies: 1177 | path-key "^2.0.0" 1178 | 1179 | number-is-nan@^1.0.0: 1180 | version "1.0.1" 1181 | resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" 1182 | 1183 | oauth-sign@~0.8.2: 1184 | version "0.8.2" 1185 | resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" 1186 | 1187 | object-assign@^4.0.1: 1188 | version "4.1.1" 1189 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 1190 | 1191 | once@1.x, once@^1.3.0: 1192 | version "1.4.0" 1193 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" 1194 | dependencies: 1195 | wrappy "1" 1196 | 1197 | onetime@^2.0.0: 1198 | version "2.0.1" 1199 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" 1200 | dependencies: 1201 | mimic-fn "^1.0.0" 1202 | 1203 | optionator@^0.8.1, optionator@^0.8.2: 1204 | version "0.8.2" 1205 | resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" 1206 | dependencies: 1207 | deep-is "~0.1.3" 1208 | fast-levenshtein "~2.0.4" 1209 | levn "~0.3.0" 1210 | prelude-ls "~1.1.2" 1211 | type-check "~0.3.2" 1212 | wordwrap "~1.0.0" 1213 | 1214 | os-locale@^2.0.0: 1215 | version "2.1.0" 1216 | resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" 1217 | dependencies: 1218 | execa "^0.7.0" 1219 | lcid "^1.0.0" 1220 | mem "^1.1.0" 1221 | 1222 | os-tmpdir@~1.0.2: 1223 | version "1.0.2" 1224 | resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" 1225 | 1226 | p-finally@^1.0.0: 1227 | version "1.0.0" 1228 | resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" 1229 | 1230 | p-limit@^1.1.0: 1231 | version "1.2.0" 1232 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" 1233 | dependencies: 1234 | p-try "^1.0.0" 1235 | 1236 | p-locate@^2.0.0: 1237 | version "2.0.0" 1238 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" 1239 | dependencies: 1240 | p-limit "^1.1.0" 1241 | 1242 | p-try@^1.0.0: 1243 | version "1.0.0" 1244 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" 1245 | 1246 | parse-ms@^1.0.0: 1247 | version "1.0.1" 1248 | resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-1.0.1.tgz#56346d4749d78f23430ca0c713850aef91aa361d" 1249 | 1250 | path-exists@^3.0.0: 1251 | version "3.0.0" 1252 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" 1253 | 1254 | path-is-absolute@^1.0.0: 1255 | version "1.0.1" 1256 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" 1257 | 1258 | path-is-inside@^1.0.1, path-is-inside@^1.0.2: 1259 | version "1.0.2" 1260 | resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" 1261 | 1262 | path-key@^2.0.0: 1263 | version "2.0.1" 1264 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" 1265 | 1266 | pathval@^1.0.0: 1267 | version "1.1.0" 1268 | resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" 1269 | 1270 | performance-now@^2.1.0: 1271 | version "2.1.0" 1272 | resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" 1273 | 1274 | pify@^2.0.0: 1275 | version "2.3.0" 1276 | resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" 1277 | 1278 | pinkie-promise@^2.0.0: 1279 | version "2.0.1" 1280 | resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" 1281 | dependencies: 1282 | pinkie "^2.0.0" 1283 | 1284 | pinkie@^2.0.0: 1285 | version "2.0.4" 1286 | resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" 1287 | 1288 | plur@^2.1.2: 1289 | version "2.1.2" 1290 | resolved "https://registry.yarnpkg.com/plur/-/plur-2.1.2.tgz#7482452c1a0f508e3e344eaec312c91c29dc655a" 1291 | dependencies: 1292 | irregular-plurals "^1.0.0" 1293 | 1294 | pluralize@^7.0.0: 1295 | version "7.0.0" 1296 | resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" 1297 | 1298 | prelude-ls@~1.1.2: 1299 | version "1.1.2" 1300 | resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" 1301 | 1302 | prettier-eslint-cli@^4.7.1: 1303 | version "4.7.1" 1304 | resolved "https://registry.yarnpkg.com/prettier-eslint-cli/-/prettier-eslint-cli-4.7.1.tgz#3d103c494baa4e80b99ad53e2b9db7620101859f" 1305 | dependencies: 1306 | arrify "^1.0.1" 1307 | babel-runtime "^6.23.0" 1308 | boolify "^1.0.0" 1309 | camelcase-keys "^4.1.0" 1310 | chalk "2.3.0" 1311 | common-tags "^1.4.0" 1312 | eslint "^4.5.0" 1313 | find-up "^2.1.0" 1314 | get-stdin "^5.0.1" 1315 | glob "^7.1.1" 1316 | ignore "^3.2.7" 1317 | indent-string "^3.1.0" 1318 | lodash.memoize "^4.1.2" 1319 | loglevel-colored-level-prefix "^1.0.0" 1320 | messageformat "^1.0.2" 1321 | prettier-eslint "^8.5.0" 1322 | rxjs "^5.3.0" 1323 | yargs "10.0.3" 1324 | 1325 | prettier-eslint@^8.5.0, prettier-eslint@^8.8.2: 1326 | version "8.8.2" 1327 | resolved "https://registry.yarnpkg.com/prettier-eslint/-/prettier-eslint-8.8.2.tgz#fcb29a48ab4524e234680797fe70e9d136ccaf0b" 1328 | dependencies: 1329 | babel-runtime "^6.26.0" 1330 | common-tags "^1.4.0" 1331 | dlv "^1.1.0" 1332 | eslint "^4.0.0" 1333 | indent-string "^3.2.0" 1334 | lodash.merge "^4.6.0" 1335 | loglevel-colored-level-prefix "^1.0.0" 1336 | prettier "^1.7.0" 1337 | pretty-format "^23.0.1" 1338 | require-relative "^0.8.7" 1339 | typescript "^2.5.1" 1340 | typescript-eslint-parser "^16.0.0" 1341 | vue-eslint-parser "^2.0.2" 1342 | 1343 | prettier@^1.13.4: 1344 | version "1.13.4" 1345 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.4.tgz#31bbae6990f13b1093187c731766a14036fa72e6" 1346 | 1347 | prettier@^1.7.0: 1348 | version "1.14.3" 1349 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.14.3.tgz#90238dd4c0684b7edce5f83b0fb7328e48bd0895" 1350 | 1351 | pretty-format@^23.0.1: 1352 | version "23.6.0" 1353 | resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760" 1354 | dependencies: 1355 | ansi-regex "^3.0.0" 1356 | ansi-styles "^3.2.0" 1357 | 1358 | pretty-ms@3.0.1: 1359 | version "3.0.1" 1360 | resolved "https://registry.yarnpkg.com/pretty-ms/-/pretty-ms-3.0.1.tgz#7c18b73c228a9b8f6edc2835a12cb8f7ed85f9f4" 1361 | dependencies: 1362 | parse-ms "^1.0.0" 1363 | plur "^2.1.2" 1364 | 1365 | pretty-quick@^1.6.0: 1366 | version "1.6.0" 1367 | resolved "https://registry.yarnpkg.com/pretty-quick/-/pretty-quick-1.6.0.tgz#afc3591eb5c4cf37614a305d489a8a40e57c9258" 1368 | dependencies: 1369 | chalk "^2.3.0" 1370 | execa "^0.8.0" 1371 | find-up "^2.1.0" 1372 | ignore "^3.3.7" 1373 | mri "^1.1.0" 1374 | 1375 | process-nextick-args@~2.0.0: 1376 | version "2.0.0" 1377 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" 1378 | 1379 | progress@^2.0.0: 1380 | version "2.0.0" 1381 | resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" 1382 | 1383 | propagate@^1.0.0: 1384 | version "1.0.0" 1385 | resolved "https://registry.yarnpkg.com/propagate/-/propagate-1.0.0.tgz#00c2daeedda20e87e3782b344adba1cddd6ad709" 1386 | 1387 | pseudomap@^1.0.2: 1388 | version "1.0.2" 1389 | resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" 1390 | 1391 | punycode@^1.4.1: 1392 | version "1.4.1" 1393 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" 1394 | 1395 | qs@^6.5.1, qs@~6.5.1: 1396 | version "6.5.2" 1397 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" 1398 | 1399 | quick-lru@^1.0.0: 1400 | version "1.1.0" 1401 | resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" 1402 | 1403 | readable-stream@^2.2.2: 1404 | version "2.3.6" 1405 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" 1406 | dependencies: 1407 | core-util-is "~1.0.0" 1408 | inherits "~2.0.3" 1409 | isarray "~1.0.0" 1410 | process-nextick-args "~2.0.0" 1411 | safe-buffer "~5.1.1" 1412 | string_decoder "~1.1.1" 1413 | util-deprecate "~1.0.1" 1414 | 1415 | regenerator-runtime@^0.11.0: 1416 | version "0.11.1" 1417 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" 1418 | 1419 | regexpp@^1.0.1: 1420 | version "1.1.0" 1421 | resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" 1422 | 1423 | request-debug@^0.2.0: 1424 | version "0.2.0" 1425 | resolved "https://registry.yarnpkg.com/request-debug/-/request-debug-0.2.0.tgz#fc054ec817181b04ca41a052c136f61c48abaf78" 1426 | dependencies: 1427 | stringify-clone "^1.0.0" 1428 | 1429 | request@^2.72.0: 1430 | version "2.87.0" 1431 | resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" 1432 | dependencies: 1433 | aws-sign2 "~0.7.0" 1434 | aws4 "^1.6.0" 1435 | caseless "~0.12.0" 1436 | combined-stream "~1.0.5" 1437 | extend "~3.0.1" 1438 | forever-agent "~0.6.1" 1439 | form-data "~2.3.1" 1440 | har-validator "~5.0.3" 1441 | http-signature "~1.2.0" 1442 | is-typedarray "~1.0.0" 1443 | isstream "~0.1.2" 1444 | json-stringify-safe "~5.0.1" 1445 | mime-types "~2.1.17" 1446 | oauth-sign "~0.8.2" 1447 | performance-now "^2.1.0" 1448 | qs "~6.5.1" 1449 | safe-buffer "^5.1.1" 1450 | tough-cookie "~2.3.3" 1451 | tunnel-agent "^0.6.0" 1452 | uuid "^3.1.0" 1453 | 1454 | require-directory@^2.1.1: 1455 | version "2.1.1" 1456 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 1457 | 1458 | require-main-filename@^1.0.1: 1459 | version "1.0.1" 1460 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" 1461 | 1462 | require-relative@^0.8.7: 1463 | version "0.8.7" 1464 | resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de" 1465 | 1466 | require-uncached@^1.0.3: 1467 | version "1.0.3" 1468 | resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" 1469 | dependencies: 1470 | caller-path "^0.1.0" 1471 | resolve-from "^1.0.0" 1472 | 1473 | reserved-words@^0.1.2: 1474 | version "0.1.2" 1475 | resolved "https://registry.yarnpkg.com/reserved-words/-/reserved-words-0.1.2.tgz#00a0940f98cd501aeaaac316411d9adc52b31ab1" 1476 | 1477 | resolve-from@^1.0.0: 1478 | version "1.0.1" 1479 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" 1480 | 1481 | resolve@1.1.x: 1482 | version "1.1.7" 1483 | resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" 1484 | 1485 | restore-cursor@^2.0.0: 1486 | version "2.0.0" 1487 | resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" 1488 | dependencies: 1489 | onetime "^2.0.0" 1490 | signal-exit "^3.0.2" 1491 | 1492 | rimraf@^2.2.8: 1493 | version "2.6.2" 1494 | resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" 1495 | dependencies: 1496 | glob "^7.0.5" 1497 | 1498 | run-async@^2.2.0: 1499 | version "2.3.0" 1500 | resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" 1501 | dependencies: 1502 | is-promise "^2.1.0" 1503 | 1504 | rx-lite-aggregates@^4.0.8: 1505 | version "4.0.8" 1506 | resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" 1507 | dependencies: 1508 | rx-lite "*" 1509 | 1510 | rx-lite@*, rx-lite@^4.0.8: 1511 | version "4.0.8" 1512 | resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" 1513 | 1514 | rxjs@^5.3.0: 1515 | version "5.5.12" 1516 | resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.5.12.tgz#6fa61b8a77c3d793dbaf270bee2f43f652d741cc" 1517 | dependencies: 1518 | symbol-observable "1.0.1" 1519 | 1520 | safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: 1521 | version "5.1.2" 1522 | resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" 1523 | 1524 | "safer-buffer@>= 2.1.2 < 3": 1525 | version "2.1.2" 1526 | resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" 1527 | 1528 | semver@5.5.0, semver@^5.3.0, semver@^5.5.0: 1529 | version "5.5.0" 1530 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" 1531 | 1532 | set-blocking@^2.0.0: 1533 | version "2.0.0" 1534 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 1535 | 1536 | shebang-command@^1.2.0: 1537 | version "1.2.0" 1538 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" 1539 | dependencies: 1540 | shebang-regex "^1.0.0" 1541 | 1542 | shebang-regex@^1.0.0: 1543 | version "1.0.0" 1544 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" 1545 | 1546 | signal-exit@^3.0.0, signal-exit@^3.0.2: 1547 | version "3.0.2" 1548 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" 1549 | 1550 | slice-ansi@1.0.0: 1551 | version "1.0.0" 1552 | resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" 1553 | dependencies: 1554 | is-fullwidth-code-point "^2.0.0" 1555 | 1556 | source-map@^0.6.1: 1557 | version "0.6.1" 1558 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" 1559 | 1560 | source-map@~0.2.0: 1561 | version "0.2.0" 1562 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" 1563 | dependencies: 1564 | amdefine ">=0.0.4" 1565 | 1566 | sprintf-js@~1.0.2: 1567 | version "1.0.3" 1568 | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" 1569 | 1570 | sshpk@^1.7.0: 1571 | version "1.14.1" 1572 | resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.1.tgz#130f5975eddad963f1d56f92b9ac6c51fa9f83eb" 1573 | dependencies: 1574 | asn1 "~0.2.3" 1575 | assert-plus "^1.0.0" 1576 | dashdash "^1.12.0" 1577 | getpass "^0.1.1" 1578 | optionalDependencies: 1579 | bcrypt-pbkdf "^1.0.0" 1580 | ecc-jsbn "~0.1.1" 1581 | jsbn "~0.1.0" 1582 | tweetnacl "~0.14.0" 1583 | 1584 | string-width@^1.0.1: 1585 | version "1.0.2" 1586 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" 1587 | dependencies: 1588 | code-point-at "^1.0.0" 1589 | is-fullwidth-code-point "^1.0.0" 1590 | strip-ansi "^3.0.0" 1591 | 1592 | string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: 1593 | version "2.1.1" 1594 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" 1595 | dependencies: 1596 | is-fullwidth-code-point "^2.0.0" 1597 | strip-ansi "^4.0.0" 1598 | 1599 | string_decoder@~1.1.1: 1600 | version "1.1.1" 1601 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" 1602 | dependencies: 1603 | safe-buffer "~5.1.0" 1604 | 1605 | stringify-clone@^1.0.0: 1606 | version "1.1.1" 1607 | resolved "https://registry.yarnpkg.com/stringify-clone/-/stringify-clone-1.1.1.tgz#309a235fb4ecfccd7d388dbe18ba904facaf433b" 1608 | 1609 | strip-ansi@^3.0.0, strip-ansi@^3.0.1: 1610 | version "3.0.1" 1611 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" 1612 | dependencies: 1613 | ansi-regex "^2.0.0" 1614 | 1615 | strip-ansi@^4.0.0: 1616 | version "4.0.0" 1617 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" 1618 | dependencies: 1619 | ansi-regex "^3.0.0" 1620 | 1621 | strip-eof@^1.0.0: 1622 | version "1.0.0" 1623 | resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" 1624 | 1625 | strip-indent@^2.0.0: 1626 | version "2.0.0" 1627 | resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" 1628 | 1629 | strip-json-comments@~2.0.1: 1630 | version "2.0.1" 1631 | resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" 1632 | 1633 | supports-color@5.4.0, supports-color@^5.3.0: 1634 | version "5.4.0" 1635 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" 1636 | dependencies: 1637 | has-flag "^3.0.0" 1638 | 1639 | supports-color@^2.0.0: 1640 | version "2.0.0" 1641 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" 1642 | 1643 | supports-color@^3.1.0: 1644 | version "3.2.3" 1645 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" 1646 | dependencies: 1647 | has-flag "^1.0.0" 1648 | 1649 | supports-color@^4.0.0: 1650 | version "4.5.0" 1651 | resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" 1652 | dependencies: 1653 | has-flag "^2.0.0" 1654 | 1655 | symbol-observable@1.0.1: 1656 | version "1.0.1" 1657 | resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4" 1658 | 1659 | table@4.0.2: 1660 | version "4.0.2" 1661 | resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" 1662 | dependencies: 1663 | ajv "^5.2.3" 1664 | ajv-keywords "^2.1.0" 1665 | chalk "^2.1.0" 1666 | lodash "^4.17.4" 1667 | slice-ansi "1.0.0" 1668 | string-width "^2.1.1" 1669 | 1670 | text-table@~0.2.0: 1671 | version "0.2.0" 1672 | resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" 1673 | 1674 | through@^2.3.6: 1675 | version "2.3.8" 1676 | resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" 1677 | 1678 | tmp@^0.0.33: 1679 | version "0.0.33" 1680 | resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" 1681 | dependencies: 1682 | os-tmpdir "~1.0.2" 1683 | 1684 | tough-cookie@~2.3.3: 1685 | version "2.3.4" 1686 | resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" 1687 | dependencies: 1688 | punycode "^1.4.1" 1689 | 1690 | tunnel-agent@^0.6.0: 1691 | version "0.6.0" 1692 | resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" 1693 | dependencies: 1694 | safe-buffer "^5.0.1" 1695 | 1696 | tweetnacl@^0.14.3, tweetnacl@~0.14.0: 1697 | version "0.14.5" 1698 | resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" 1699 | 1700 | type-check@~0.3.2: 1701 | version "0.3.2" 1702 | resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" 1703 | dependencies: 1704 | prelude-ls "~1.1.2" 1705 | 1706 | type-detect@0.1.1: 1707 | version "0.1.1" 1708 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-0.1.1.tgz#0ba5ec2a885640e470ea4e8505971900dac58822" 1709 | 1710 | type-detect@^1.0.0: 1711 | version "1.0.0" 1712 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-1.0.0.tgz#762217cc06db258ec48908a1298e8b95121e8ea2" 1713 | 1714 | type-detect@^4.0.0: 1715 | version "4.0.8" 1716 | resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" 1717 | 1718 | typedarray@^0.0.6: 1719 | version "0.0.6" 1720 | resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" 1721 | 1722 | typescript-eslint-parser@^16.0.0: 1723 | version "16.0.1" 1724 | resolved "https://registry.yarnpkg.com/typescript-eslint-parser/-/typescript-eslint-parser-16.0.1.tgz#b40681c7043b222b9772748b700a000b241c031b" 1725 | dependencies: 1726 | lodash.unescape "4.0.1" 1727 | semver "5.5.0" 1728 | 1729 | typescript@^2.5.1: 1730 | version "2.9.2" 1731 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" 1732 | 1733 | uglify-js@^3.1.4: 1734 | version "3.10.3" 1735 | resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.10.3.tgz#f0d2f99736c14de46d2d24649ba328be3e71c3bf" 1736 | 1737 | util-deprecate@~1.0.1: 1738 | version "1.0.2" 1739 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 1740 | 1741 | uuid@^3.1.0: 1742 | version "3.2.1" 1743 | resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" 1744 | 1745 | verror@1.10.0: 1746 | version "1.10.0" 1747 | resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" 1748 | dependencies: 1749 | assert-plus "^1.0.0" 1750 | core-util-is "1.0.2" 1751 | extsprintf "^1.2.0" 1752 | 1753 | vue-eslint-parser@^2.0.2: 1754 | version "2.0.3" 1755 | resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-2.0.3.tgz#c268c96c6d94cfe3d938a5f7593959b0ca3360d1" 1756 | dependencies: 1757 | debug "^3.1.0" 1758 | eslint-scope "^3.7.1" 1759 | eslint-visitor-keys "^1.0.0" 1760 | espree "^3.5.2" 1761 | esquery "^1.0.0" 1762 | lodash "^4.17.4" 1763 | 1764 | which-module@^2.0.0: 1765 | version "2.0.0" 1766 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 1767 | 1768 | which@^1.1.1, which@^1.2.9: 1769 | version "1.3.1" 1770 | resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" 1771 | dependencies: 1772 | isexe "^2.0.0" 1773 | 1774 | wordwrap@^1.0.0, wordwrap@~1.0.0: 1775 | version "1.0.0" 1776 | resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" 1777 | 1778 | wrap-ansi@^2.0.0: 1779 | version "2.1.0" 1780 | resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" 1781 | dependencies: 1782 | string-width "^1.0.1" 1783 | strip-ansi "^3.0.1" 1784 | 1785 | wrappy@1: 1786 | version "1.0.2" 1787 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" 1788 | 1789 | write@^0.2.1: 1790 | version "0.2.1" 1791 | resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" 1792 | dependencies: 1793 | mkdirp "^0.5.1" 1794 | 1795 | xml@^1.0.0: 1796 | version "1.0.1" 1797 | resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" 1798 | 1799 | y18n@^3.2.1: 1800 | version "3.2.1" 1801 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" 1802 | 1803 | yallist@^2.1.2: 1804 | version "2.1.2" 1805 | resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" 1806 | 1807 | yargs-parser@^8.0.0: 1808 | version "8.1.0" 1809 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" 1810 | dependencies: 1811 | camelcase "^4.1.0" 1812 | 1813 | yargs@10.0.3: 1814 | version "10.0.3" 1815 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.0.3.tgz#6542debd9080ad517ec5048fb454efe9e4d4aaae" 1816 | dependencies: 1817 | cliui "^3.2.0" 1818 | decamelize "^1.1.1" 1819 | find-up "^2.1.0" 1820 | get-caller-file "^1.0.1" 1821 | os-locale "^2.0.0" 1822 | require-directory "^2.1.1" 1823 | require-main-filename "^1.0.1" 1824 | set-blocking "^2.0.0" 1825 | string-width "^2.0.0" 1826 | which-module "^2.0.0" 1827 | y18n "^3.2.1" 1828 | yargs-parser "^8.0.0" 1829 | --------------------------------------------------------------------------------